• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Messy error messages across pcall()

Participant ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

I'm trying to clean up error handling in a plugin I'm writing and I see behavior I don't understand:

I'm in function A started with postAsyncTaskWithContext() and at the top of function A I call LrDialogs.attachErrorDialogToFunctionContext( context ).

- If I then throw an error from function A using LrErrors.throwUserError( 'test' ), I get a nice clean dialog box with the contents "test". Great - as expected.

- If I pcall() another function B from function A and call LrErrors.throwUserError( 'test' ) from B, the contents of the dialog box are "[string 'myfile.lua']:123: <AgErrorText>test</AgErrorText>". Not great. Any ideas why pcall is causing this?

Also: both dialog boxes happen twice - dismiss it and it comes right back with the same contents a second time. Dismiss that and it goes away.

If it helps, I am using johnrellis​'s debugging toolkit and my plugin is currently named with .lrdevplugin. Function A is actually invoked with :

     LrFunctionContext.postAsyncTaskWithContext( 'myMain', Debug.showErrors ( myMain ) )

I also observe that when I pcall B from A, if I wrap that with LrTasks.pcall( Debug.showErrors( B ) ) then I get 3 error dialogs instead of 2 and the first one is clean and the last two are messy.

Hopefully this is some combo of me not understanding proper flow in LR error handling and/or John's Debug toolkit?

Thanks,

db

TOPICS
SDK

Views

1.2K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

Hmm, I'm not able to reproduce that behavior with a test script.  But if you could produce a stripped down sample script showing the behavior, I'll dig into it and produce accurate diagnoses and recommendations. Some initial thoughts and questions:

- Are you using the latest version of my Debugging Toolkit, 1.7, or do you have an earlier version?

- None of task handling and error handling in LR is well-documented (and that goes for the toolkit as well). The toolkit does tries to work with this undocumented behavior as well as it can, which may be imperfect in your case, but I'm not sure.

- LrErrors.throwUserError (e) wraps "e" with <AgErrorText>e</AgErrorText>.  Here's the actual Lua error strings thrown by error() and throwUserError():

error ("xxx") => "[string \"dfburns.lua\"]:16: xxx"

throwUserError ("xxx") => "[string \"dfburns.lua\"]:17: <AgErrorText>xxx</AgErrorText>"

When the code invoked by LrDialogs.attachErrorDialogToFunctionContext() catches the error using pcall(), it parses it.  If it finds <AgErrorText>e</AgeErrortText>, it shows just "e" in the dialog, otherwise it shows "internal error" followed by the entire error string.

- You said your plugin currently ends with ".lrdevplugin". When the toolkit is initialized with Debug.init(), then if the plugin directory ends with ".lrdevplugin", showErrors() will invoke the debugger when errors are caught, and when the plugin directory ends with ".lrplugin" (i.e. in production), showErrors() will handle errors with LrDialogs.attachErrorDialogToFunctionContext().   Since you're not seeing the debugger invoked, it sounds like you've explicitly disabled debugging with Debug.init (false)?

- Since showErrors() itself invokes attachErrorDialogToFunctionContext(), you shouldn't use both in the same task.

- Don't use LrTasks.pcall (showErrors (f)).  Do either LrTasks.pcall (f) or Debug.pcall (f).  (This is buried in the documentation for the toolkit.) First, if you're invoking pcall(), by definition you want to catch and handle the error right there explicitly without invoking a default error handler.  Second, Debug.pcall() allows the toolkit debugger to do stack tracing, breakpoints, and stepping through the pcall(), whereas LrTasks.pcall() does not.  (Believe it or not, internally LrTasks.pcall() creates an additional Lua task in which to call the function, probably something to do with LR's task scheduler.)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

Sounds like I need to make a few adjustments to my code. Once I do those and can still reproduce the behavior, I'll try to post a test script. To answer your points:

- I was not up to 1.7 of Debug.lua but am now.

- My plugin currently ends in .lrdevplugin. Right now, I want to test how my error handling will look to an end-user without having to rename to .lrplugin. Am I correct that passing false to Debug.init() is the right way to do this test? (Now that I'm on 1.7, I'm having trouble avoiding the debugger window when I just want Lr's error dialog to show. Looking around for why that is...)

- When you say "Since showErrors() itself invokes attachErrorDialogToFunctionContext(), you shouldn't use both in the same task." For my clarity, you mean I shouldn't wrap a task function with Debug.showErrors and then inside that function call attachError... Correct?

- For your last point, what about the scenario where I want to catch an error but then immediately throw a new one with a better error message that is ok for a default handler to catch? What's best practice for this when trying to take advantage of the debug toolkit?

db

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

- My plugin currently ends in .lrdevplugin. Right now, I want to test how my error handling will look to an end-user without having to rename to .lrplugin. Am I correct that passing false to Debug.init() is the right way to do this test?

That's correct.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

- When you say "Since showErrors() itself invokes attachErrorDialogToFunctionContext(), you shouldn't use both in the same task." For my clarity, you mean I shouldn't wrap a task function with Debug.showErrors and then inside that function call attachError... Correct?

Right.  Use one or the other at the top level of a task.  Since Debug.showErrors() uses attachErrorDialogToFunctionContext() when in production (Debug.init (false) or ".lrplugin"), it's usually simpler (and far fewer characters) to use Debug.showErrors().

Here's the version of showErrors() when debugging is disabled:

function showErrors (func)

    return function (...)

        return LrFunctionContext.callWithContext("wrapped",

            function (context)

                LrDialogs.attachErrorDialogToFunctionContext (context)

                return func (unpack (arg))

                end)

        end

    end

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

- For your last point, what about the scenario where I want to catch an error but then immediately throw a new one with a better error message that is ok for a default handler to catch? What's best practice for this when trying to take advantage of the debug toolkit?

If I understand you correctly, you'd use the following code:

local pcall = Debug.pcall -- I declare this at the top of a module for conciseness

...

local success, err = pcall (myFunction, myArgs)

if not success then

    LrErrors.throwUserError ("better error message")

    end

This should work whether you have Debug.showErrors() or attachErrorDialogToFunctionContext() at the top of a task.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

Here is a test script that shows a problem but now it's slightly different. The throwUserError in funcA shows a clean error message as expected (or the debug window if Debug is initted with 'true'). But comment that out and let the pcall to funcB happen and I never see funcB's error message. If I put an LrDialogs.message() in funcB before the throwUserError(), I see that so I know funcB is being invoked.

This behavior is blocking me from trying to show you the messy error message I expected from funcB. My full plugin code still does that but I'm not sure (yet) the difference between that and the code below.

require 'strict'

local Debug = require 'Debug'.init( false )

local LrFunctionContext = import 'LrFunctionContext'

local LrTasks = import 'LrTasks'

local LrErrors = import 'LrErrors'

local function funcB()

    LrErrors.throwUserError( 'error in funcB' )

end

local function funcA( context )

    LrErrors.throwUserError( 'error in funcA' )

    LrTasks.pcall( funcB )

end

LrFunctionContext.postAsyncTaskWithContext( 'funcA', Debug.showErrors ( funcA ) )

Thanks for taking a look,

db

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

I'm looking at it now...strange.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

I got a little confused because of Debug.init (false).  When I enabled the debugger, it was immediately clear what's going on. The call to pcall (funcB) is catching the error and causing it be ignored. (That's what pcall() is supposed to do.)

When I enabled the debugger and inserted a call to Debug.pause():

require 'strict'

local Debug = require 'Debug'.init( true )

local LrFunctionContext = import 'LrFunctionContext'

local LrTasks = import 'LrTasks'

local LrErrors = import 'LrErrors'

local function funcB()

    LrErrors.throwUserError( 'error in funcB' )

end

local function funcA( context )

    -- LrErrors.throwUserError( 'error in funcA' )

    local success, err = LrTasks.pcall( funcB )

    Debug.pause ()

end

LrFunctionContext.postAsyncTaskWithContext( 'funcA', Debug.showErrors ( funcA ) )

...I saw that pcall() is returning false and the error string, as expected:

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

Ah, silly error on my part. I got wrapped up in trying to create a simple test case. I get the same results if I log success and err to the log (strangely, I can't get the debugger window to show any other values for those than nil, even though my test case now looks like yours).

So, I might rework things to get rid of funcA using pcall() to call funcB but if I don't, and if I want to display the error message coming back from funcB, do you agree that I need to parse this myself?

db

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

So, I might rework things to get rid of funcA using pcall() to call funcB but if I don't, and if I want to display the error message coming back from funcB, do you agree that I need to parse this myself?

I think I've lost the context at this point -- perhaps rephrase the question?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

What I meant was, if I do a pcall to funcB and capture the thrown error message, the message's format is "[string "errorMsgTest.lua"]:10: <AgErrorText>error in funcB</AgErrorText>". I think there's no formal way to deal with that if I want to extract the embedded message, i.e. I'll need to parse it out myself, yes?

db

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 29, 2017 Nov 29, 2017

Copy link to clipboard

Copied

If I do a pcall to funcB and capture the thrown error message, the message's format is "[string "errorMsgTest.lua"]:10: <AgErrorText>error in funcB</AgErrorText>". I think there's no formal way to deal with that if I want to extract the embedded message, i.e. I'll need to parse it out myself, yes?

Right.

In my own modules, I generally don't use errors for anything other than true programming errors.  For "exceptional" results, e.g. a module not being to open an expected file, I use the style of returning an additional string result "err", which if non-nil contains a human-readable error message indicating something exceptional happened. 

While this can entail more checking of return results for non-nill err's, it has the advantage that programming errors won't be accidentally masked by a pcall() catching an "exceptional" (intended) error.  Languages like Java with more sophisticated exception handling let you easily distinguish programming errors from exceptional conditions, but not Lua.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Dec 11, 2017 Dec 11, 2017

Copy link to clipboard

Copied

LATEST

Just realized I never closed the loop on this. I agree that I'd probably be better off if I conformed to the Lua idiom. I think proof of your point is that Adobe has to put markers (<AgErrorText>) in the error string as a way to detect different exception types.

Thanks for all the experience and advice.

db

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines