In general, the no-such-identifier message shows the line number, but there are exceptions, such as when the bad identifier is in the label or command on the popups menu.

If you think it's a control code touching the identifier, try pasting the next command into the editbox but don't press enter until you've use the ctrl-button to include control codes into the clipboarded text. Then see if there's anything suspicious in that line.

You can also try to find the problem characters using /filter. This example searches for the bold code:

//filter -fs $qt($script(1)) * $+ $chr(2) $+ *

You can find the ascii values for the control codes by pasting them inside the parenthesis. //echo -a $asc()

Don't forget about the TAB $chr(9), as I've seen this touching identifiers and invalidating them.

Your solution for unknown command won't work, because this is generated by a server message, not by a script line. When you use an invalid /command, after failing to match it to an alias or a built-in command, mirc sends to the $server in hopes it knows what to do with it. After an unknown delay, the server answers back with the raw 421 of the invalid command.

What would help is if the error message included the *full* reply sent by the server. It includes the commandline sent along with the bad command, and that often can help figure out where the mistake is located.

You can also use a debugging window to snoop at the messages to/from the server. Here's a couple sample /debug commands to show the bad command error message coming from the server. You can then scroll up a little higher to find how much earlier you sent the bad command to it. The different colors helps you more easily find mirc's chatter amongst the larger volume of return messages.

The 2nd message shows the hex value of each character in $parms, which can help identify control codes touching the outbound command. Of course as you've implied, $ticks is not the same thing as milliseconds, but showing the lowest 3 digits of $ticks helps identify partial-seconds intervals between events. I prefer seeing the digits in hex because it's easier to read when the display is a fixed 2 digits instead of a variable 1-3. With experience, you can eventually be able to identify certain ranges, such as how the numbers 0-9 are hex 31-39.

Code:
debuggvm  { !window -ze2Dj5000k @debug | !titlebar @debug active= $+ $~scid($~window(@debug).cid).network logging: $~addtok($~gettok($~window(@debug).titlebar,3-,32),$~network,32) | !debug -pir44o52 @debug $!+([,$~time,.,$~right($~ticks,3),],[,$~cid,],[,$~network,] $~!1 ) } ; Raccoon's altered by maroon v7.52+
debuggv6  { !window -ze2Dj5000k @debug | !titlebar @debug active= $+ $~scid($~window(@debug).cid).network logging: $~addtok($~gettok($~window(@debug).titlebar,3-,32),$~network,32) | !debug -pi @debug $!+($~chr(3),$~iif(->* iswm $~!1,4),[,$~time,.,$~right($~ticks,3),],[,$~cid,],[,$~network,],$~chr(15) $~!1 ) } ; Raccoon's altered by maroon pre7.53
debuggmh1 { !window -ze2Dj5000k @debug | !titlebar @debug active= $+ $~scid($~window(@debug).cid).network logging: $~addtok($~gettok($~window(@debug).titlebar,3-,32),$~network,32) | !debug -pir44o52 @debug $!+([,$~time,.,$~right($~ticks,3),],[,$~cid,],[,$~network,] $~!1 hex: $~!regsubex($~1,/(.)/g,$~base($~asc(\t),10,16,2) $~!+ $~!chr(32)) ) } ; Raccoon's altered by maroon+hex



And a reminder for other readers, the debug command only captures events from the network where you run this command, so you can either place the command in your perform-on-connect for all networks, or only run it when you have problems to fix. I rarely run the hex version except when trying to chase down evasive bugs.