Possible improvement to error message — what do you think?

I’m trying to tidy up the error message because they are often longer than the post window and hide the important parts all the way at the top, I’ve also got it to show where the error might be. What do you think? is there anything else that should be here? should I format it differently?

Please scroll all the way down in the code box as you would see the bottom first in the post window.

When running this code…

(
SynthDef(\asdf, {
	SinOsc.ar();
	SinOsc.ar();
	SinOsc.ar();
	SinOsc.ar();
	SinOsc.ar();
	SinOsc.ar();
	2352.asdf([1, 2, 3], \asdf) 
}).add
)

…the current error message generated is (scroll to the bottom first)…

_______________________________________________________________
Protected backtrace for DoesNotUnderstandError...

PROTECTED CALL STACK:
	... I haven't squished the protected bracktrace yet ...
	
Backtrace for DoesNotUnderstandError
	0: Object:throw - 0x560630819240
		args this = DoesNotUnderstandError.new;
	1: Function:protect - 0x560631c0a000
		args this = { "open Function" }, handler = { UGen.buildSynthDef = nil; };
		vars result = DoesNotUnderstandError.new;
	2: SynthDef:build - 0x5606324c8d00
		args rates = nil, prependArgs = nil, this = SynthDef('asdf'), 
			ugenGraphFunc = { SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); 235 ... };
	3: < closed FunctionDef > - 0x560633a272a8
	4: Interpreter:interpretPrintCmdLine - 0x560634652c40
		args this = Interpreter.new;
		vars doc = nil, res = nil, ideClass = ScIDE, 
			code = "( SynthDef(\\asdf, { SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinOsc.ar ... ", 
			func = { SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); 235 ... };
	5: Process:interpretPrintCmdLine - 0x5606345e8d40
		args this = thisProcess;

Possible offending code in frame 4: 
(
SynthDef(\asdf, {
	SinOsc.ar();
	SinOsc.ar();
	SinOsc.ar();
	SinOsc.ar();
	SinOsc.ar();
	SinOsc.ar();
	2352.asdf([1, 2, 3], \asdf);
	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ maybe this was a mistake?
}).add
)

Suggestion: Perhaps you misspelled 'asin', or meant to call 'asdf' on another receiver?

ERROR: Integer '2352' did not understand method 'asdf' and was called with: [1, 2, 3], 'asdf'

The code is a mess right now, and there are some weird formatting bugs, but eventually, I’ll turn it into a quark if people want to test it before I make a PR (if this is deemed an improvement).

5 Likes

Anything that improves the obscure error messages would be welcome as far as I’m concerned…

4 Likes

That’s good intiative.

Just some ideas on formating:

	SinOsc.ar();
	SinOsc.ar();
	2352.asdf([1, 2, 3], \asdf);
	^^^^^^^^^^ 
    Error: Method Not Found
}).add
)

*line*, *colum*: error: Attempted method call: 2352.asdf([1, 2, 3], \asdf)

The program attempted to call a method named 'asdf' on an integer (2352), but
such a method does not exist for integer values in SuperCollider. This occurred
in the SynthDef definition for 'asdf'.

Suggested Fixes:

- If 'asdf' is a custom method, please ensure it is defined before
  use and applicable to the object it's being called on.
- Check for typos in the method name. If you intend to use a built-in method,
  verify that it exists and is correctly spelled.
- Ensure you are calling 'asdf' on the correct kind of object. Methods in
  SuperCollider are specific to kinds of objects, and integers have limited method
  support.

For inspiration, there are some materials about this particular issue.

From simple guidelines for Dr. Racket, targeting students:

Up to dissertations on this topic (not applicable to SC, but similar ones should be available):

Also, in the backtrace itself, code as one-liner are best?

code = "( SynthDef(\\asdf, { SinOsc.ar(); SinOsc.ar(); SinOsc.ar(); SinO.....

I’m a little hesitant to write definitive statements regarding the errors locations, because the only way I’ve found to locate the position is to search through all code in the stack (passed in Function object’s source code, and methods which I haven’t implemented yet) and calculate the ‘string edit distance’ line by line between what is expected and what was found.

This means it is always a guess, right now I’m just returning the guesses that are significant (usually the top one) - a term I’ll need to think about a bit more.

I don’t think it is possible to get the exact location.

Likewise I don’t know how useful providing the file, and line number will be as the user could have removed that code from the file, and I’d have to search through the open file.

I do like the longer lists of suggestions. I wonder if these could be tied into the help documentation in some way?

It a shame the terminal won’t let you have links, colour, and other formatting. Even the ansi colour codes would be useful, but I don’t think that’s an option with windows?

1 Like

If you know the line number and file, you can have links like that in the terminal.

One thing that helps immensely is to take care of basic formatting, like using newlines instead of long lines.

Also add plain English messages with the information available is way better already.

This would require every byte code to be tagged with the file location, which is a big architectural change. I can’t think of any other way for the interpreter to get from “something happened at this byte code location” to “it lives here in xyz file.”

Also what if f = { } is halfway through a file, but I submitted only the relevant bit to the interpreter?

hjjh

Sure. I understand it’s not possible to do this way you described. With new tooling, one could find a way to point the filename and line number to the lines containing “XYZ” without structural rewriting.

In any case, linking documentation in that kind of explanation message could be helpful.

I was trying to give ideas.

FWIW I agree, it would be great to have a specific pointer to code locations. I’m just not clever enough to see how to do it.

Another problem is that users can edit code files without re-evaluating all of it, so if we encoded line numbers into functiondefs, there’s no guarantee of those line numbers being up to date. It’s a nasty problem.

hjh

You’re 100% right about how complex it would be doing it rewriting parser, interpreter, etc, etc. It would be insane.

But nowadays, there are so many toolings doing the same thing inside the editor, that maybe there are options.

In the case of methods in the class library this is possible right now as the interpreter does store the source code and file name. User code isn’t attached to a file, but can be displayed verbatim as it is stored with a string.

Attaching the file name and line number to the functiondef might not be that hard. Although it won’t provide an exact location, it gives a good place to start searching (I will investigate…).

1 Like

Today I read somewhere that vscode will parse the terminal output for things that look like links and insert clickable links, so this might be possible in vscode (issue with finding the location aside for now).

Ctrl+Click does that on vscode

Emacs also does that with find-file-at-point, which also can work on terminals with a package

Just a little update on finding the location of error, I haven’t done that formatting and friendlier error messages just yet.

For some borked class…

BorkedClass {
	*throwArg {|a|
		1;
		2;
		3;
		4;
		5;
		6;
		7;
		a.asdfasdfasdfasdf;
		8;
		9;
		10;
		11;
		12;
	}

	*throwArgPerform {|a, mes|
		1;
		2;
		3;
		4;
		5;
		6;
		7;
		a.perform(mes);
		8;
		9;
		10;
		11;
		12;
	}

	*throwVariable {|a|
		var b = 'good variable';
		"someting".postln;
		a + 1;
		b.bad_message
		//a.ahahahahaha(42)
	}

	*throwInternal {
		\asdf.dfghjkldfghjklfghjklsdfg
	}

	*throwFunc {|f|
		f.()
	}
}

The following errors are currently being produced (omitting the backtrace)

BorkedClass.throwArg(1);

...

In file /home/jordan/Work/software/quarks/Piping/Classes/ScaleExt.sc: line 10
BorkedClass:throwArg:
		5;
		6;
		7;
		a.asdfasdfasdfasdf;
        ^^^^^^^^^^^^^^^^^^^
		8;
		9;
		10;

Variable 'a' with value 1 (Integer), did not understand the message 'asdfasdfasdfasdf'.
BorkedClass.throwArgPerform(12, 'asdf');

...

In file /home/jordan/Work/software/quarks/Piping/Classes/ScaleExt.sc: line 26
BorkedClass:throwArgPerform:
		5;
		6;
		7;
		a.perform(mes);
        ^^^^^^^^^^^^^^^
		8;
		9;
		10;

Message 'asdf' contained in variable mes was not understood by 12 (Integer).
BorkedClass.throwVariable(1);

...

In file /home/jordan/Work/software/quarks/Piping/Classes/ScaleExt.sc: line 38
BorkedClass:throwVariable:
		var b = 'good variable';
		"someting".postln;
		a + 1;
		b.bad_message
        ^^^^^^^^^^^^^
		//a.ahahahahaha(42)
	}

Variable 'b' with value good variable (Symbol), did not understand the message 'bad_message'.
BorkedClass.throwInternal

...

In file /home/jordan/Work/software/quarks/Piping/Classes/ScaleExt.sc: line 43
BorkedClass:throwInternal:
	*throwInternal {
		\asdf.dfghjkldfghjklfghjklsdfg
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	}

'asdf' with value asdf (Symbol), did not understand the message 'dfghjkldfghjklfghjklsdfg'.

The case where the user’s passed in function throws hasn’t been fixed yet, but it is doable.

BorkedClass.throwFunc({1.asdf})

...

In file /home/jordan/Work/software/quarks/Piping/Classes/ScaleExt.sc: line 46
BorkedClass:throwFunc:
	*throwFunc {|f|
    ^^^^^^^^^^^^^^^
		f.()
	}

1 with value 1 (Integer), did not understand the message 'asdf'.

There are cases where this could provide false positives. Right now I am comparing object identity (===) with the variables in the frame to deduce variable and argument names, this will not work 100% for primitives.
Likewise, finding the location of the code is somewhat of a guessing game, but seems possible.

…and all of this only works on DoesNotUnderstand.

Nevertheless, I think it is possible to have something like this in supercollider today,
although this would all be trivial if some form of AST node was exposed.

1 Like

I think your format is good, I can see improvement already!

(Right now I’m writing precisely the error handling in an interpreter, it’s incredible how complex it is, and has to be implemented everywhere (io, parsing, evaluation, monad transformer). I just want to NOT DO IT AT ALL.)

Message 'asdf' contained in variable mes was not understood by 12 (Integer).

Plain English, that’s great. But this one was funny the symbol and method switched the single quotes,

Variable 'b' with value good variable (Symbol), did not understand the message 'bad_message'.

There is a project out there with a parser kind of working, but it would be strange to use it in this context to parse again the code.

When the messages get more mature, it would be interesting to consider wrapping all lines of prose, inserting a newline char at every ~70 chars. Like fill-paragraph in emacs. It just looks better formatting.

I agree this is a good initiative!

I’m not sure if you’re just concerned with pinpointing the error, or if you’re looking to generally improve error messages. But just cleaning up formatting would go a long way. Supporting colorized text multi-line errors and warnings would allow for more compact messages that are easier to read at a glance and less likely to exceed the post window bounds. E.g. in your example, formatting it as

ERROR: Integer '2352' did not understand method 'asdf' 
       called with: [1, 2, 3], 'asdf'

would probably fit the post window and have some useful hierarchical semantics, but the second line wouldn’t be colorized red.

Many times the full backtrace is overkill, so something like an IDE config setting for levels of error verbosity would support more users’ preferences. Something like: 1: just the error/warning message, 2: backtrace with only the list of function calls, 3: full verbosity with args and vars state, etc.

This would be useful for beginners whose eyes glaze over at the full call stack, live coding performance situations where you want minimal indication of an error without the detailed noise, etc.

This is what I’m working on now. I’ve got a messy but functional implementation for the DoesNotUnderstand error. I need to think over what the other types of exceptions might print and how to unify the code searching functions.

I hadn’t thought of that, but I think its a good idea!

I’m not sure how the colours are done. Are they a QT IDE thing? I know nothing about terminals, does any one know how colour codes work? are they cross platform? could supercollider be made to support them?

This is my main concern. Many unis (in the UK at least) teach max or puredata, but it has terrible accessibility support (particularly for those using screen readers), Supercollider (particularly when running in vscode) has excellent support… but is in-accessible for an entirely different reason.

1 Like

Just using shell, you can get color like this:


RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color


echo -e "${RED}This text is red.${NC}"
echo -e "${GREEN}This text is green.${NC}"
echo -e "${YELLOW}This text is yellow.${NC}"
echo -e "${BLUE}This text is blue.${NC}"