When things go wrong
Things fail. A file isn’t where you expected, a web service is down, you divide by zero. This chapter shows what MaSH does and how to take control.
What happens by default
Section titled “What happens by default”MaSH doesn’t stop when a line fails. It notes the error and carries on with the next line:
printline "start"set result to 10 / 0printline "still running"startstill runningprintline("start")result = 10 / 0printline("still running")startstill runningIn the editor, the failed lines are listed under Unhandled errors below the output. In Settings > MaSH > Logs, the run shows the same list. A script that “worked” but has unhandled errors in its log probably didn’t do what you meant.
Catching an error
Section titled “Catching an error”Put an error block straight after the line that might fail. If that line fails, the block runs:
printline "start"set result to 10 / 0error printline "Error on line {{mash.error.line}}: {{mash.error.message}}" exitendprintline "not reached"startError on line 2: Division by zeroprintline("start")result = 10 / 0error { printline("Error on line {{mash.error.line}}: {{mash.error.message}}") exit}printline("not reached")startError on line 2: Division by zeroInside the block, mash.error describes what went wrong:
| Property | What it holds |
|---|---|
mash.error.message | what went wrong, in words |
mash.error.line | the line number in your script |
mash.error.expression | the line of script that failed |
exit stops the script and counts it as a success. Leave exit out, and the script carries on after the error block.
Raising your own error
Section titled “Raising your own error”throw "message" records an error of your own and stops the block it is in. The script that called that block then skips ahead to its next error block. An error block catches a throw from a block it called, not from a line above it in the same block. If nothing catches it, the run ends and the message is the run’s error.
block checkAge age if age is less than 0 throw "Age cannot be negative" end printline "age is fine"end
checkAge(-5)printline "not reached"error printline "Caught: {{mash.error.message}}"endCaught: Age cannot be negativeblock checkAge(age) { if (age < 0) { throw "Age cannot be negative" } printline("age is fine")}
checkAge(-5)printline("not reached")error { printline("Caught: {{mash.error.message}}")}Caught: Age cannot be negativeOne handler for the whole script
Section titled “One handler for the whole script”Rather than an error block after every line, name a block as the script’s error handler by setting errorblock:
set errorblock to "onError"
block onError printline "Oops: {{mash.error.message}} (line {{mash.error.line}})"end
set a to 1 / 0printline "carried on"Oops: Division by zero (line 7)carried onerrorblock = "onError"
block onError() { printline("Oops: {{mash.error.message}} (line {{mash.error.line}})")}
a = 1 / 0printline("carried on")Oops: Division by zero (line 7)carried onThe production scripts our customers run start with a handler like this one, and use it to print a line for the log.
Stopping on purpose
Section titled “Stopping on purpose”Three ways to stop, each meaning something different to whoever reads the log:
returnends the script quietly. Use it when there’s simply nothing to do.exitends the script and records a success.fail "message"ends the script and records a failure, with your message.
printline "checking"set found to falseif not found printline "nothing to do" returnendprintline "never reached"checkingnothing to doprintline("checking")found = falseif (not found) { printline("nothing to do") return}printline("never reached")checkingnothing to dofail is the one to use when something is wrong and a person should hear about it. It shows in Logs as a failed run:
set share to mash.config.get("DEST_SHARE")if not share fail "DEST_SHARE is not set on this automation."endshare = mash.config.get("DEST_SHARE")if (not share) { fail "DEST_SHARE is not set on this automation."}Checking before you go on
Section titled “Checking before you go on”ensure is a tidy way to say “this must be true, or stop”. If the condition is false, the else part runs:
set count to 0ensure count is more than 0 else printline "Nothing to process" returnendprintline "Processing..."Nothing to processcount = 0ensure (count > 0) else { printline("Nothing to process") return}printline("Processing...")Nothing to processThe else part has to leave the script or the loop: it ends with return, next, break, fail or exit. MaSH refuses to run a script where it doesn’t.
Guarding against empty values
Section titled “Guarding against empty values”Most real errors come from a value that isn’t there. Combine ?? with a check:
set person to {name: "Ada"}set phone to person["phone"] ?? ""if mash.str(phone).length() is 0 printline "No phone number"endNo phone numberperson = {name: "Ada"}phone = person["phone"] ?? ""if (mash.str(phone).length() == 0) { printline("No phone number")}No phone numberTry it
Section titled “Try it”Add an errorblock to one of your earlier scripts. Then cause an error on purpose, such as dividing by zero, and check that your handler runs.
Next, you’ll leave the sandbox of numbers and text and start working with real files.