Every command in the family reports a failure exactly once, on stderr, whether it came from moodle, edstem or ontrack. In a table-formatted run that’s a plain line; in --json or --yaml, or whenever stdout isn’t a terminal, it’s one structured object:
hint is present only when the error has one to give. Nothing else is printed to stdout on a failing run — a script can rely on stdout being either the successful result or empty.

Exit codes

This table, and the error code vocabulary below, are stable across releases, so scripts written against one version keep working across minor and patch releases.

The error code vocabulary

There are eight error codes, each mapped to one exit code above:

Product-specific codes

Moodle adds one code outside this list: ambiguous, exit code 2, when a unit or section reference matches more than one candidate. It’s the same shape as any other error, with a candidates array attached so a caller can present the choice instead of retrying blind:
OnTrack maps its own error categories onto this same shared vocabulary before printing the error, the same way moodle and edstem do. What you see in error.code from any of the three CLIs is always one of the eight codes above, plus moodle’s ambiguous.

What an agent should do per code

  • usage (2): Fix the call — check hint, which for a missing argument carries the full usage line and argument descriptions. Don’t retry unchanged.
  • auth (3): Re-run the CLI’s sign-in command (moodle auth login, edstem auth login, ontrack auth login) before retrying.
  • config (1): A required setting is missing (commonly a base URL). Set it, then retry.
  • not_found (4) / ambiguous (2, moodle only): Don’t guess. For not_found, check the spelling or ask the user; for ambiguous, present candidates and let the user or a picker choose.
  • upstream (5): The platform itself refused the request (permissions, a closed submission window, a disabled service). Report the message; retrying identically won’t help.
  • network (1): Transient — safe to retry with backoff.
  • cancelled (130): The user (or you, acting for them) backed out of a prompt. Don’t retry automatically.
  • unexpected (1): Treat as a bug; include the message when reporting it.

Mutations

Why a non-interactive mutation without --yes is a usage error rather than a hang.