Error Codes

Structured errors for terminal output, tests, and MCP repair loops.

4 min read 808 words

Milo errors carry namespaced codes and optional repair context. Human CLIs format them for the terminal; MCPtools/callresponses expose the same context in errorDataso agents can retry with corrected arguments.

Base Type

All Milo errors derive fromMiloError:

from milo import ErrorCode, MiloError

raise MiloError(
    ErrorCode.FRM_VALIDATION,
    "Environment is required",
    argument="environment",
    constraint={"minLength": 1},
    suggestion="Provide a non-empty environment name.",
)

Constructor fields:

Field Type Purpose
code ErrorCode Stable namespaced error code
message str Human-readable explanation
suggestion str Optional next step
context dict[str, object] Extra structured diagnostics
docs_url str Optional documentation link
argument `str None` Parameter that failed
constraint `dict None` JSON-Schema-style constraint that failed

Error Classes

Exception Subsystem
InputError Raw mode, key reading, escape parsing
StateError Reducers, dispatch, sagas, combined state
FormError Form fields, validation, submit behavior
AppError App lifecycle, rendering, templates
FlowError Flow screens and transitions
DevError Dev server, file watching, hot reload
ConfigError Config parsing, merging, validation, missing files
PipelineError Pipeline phases, timeouts, dependencies
PluginError Plugin loading and hooks

Error Codes

Code Name Meaning
M-INP-001 INP_RAW_MODE Failed to enter or restore raw terminal mode
M-INP-002 INP_ESCAPE_PARSE Failed to parse an escape sequence
M-INP-003 INP_READ Failed while reading terminal input
M-STA-001 STA_REDUCER Reducer raised or returned invalid state
M-STA-002 STA_DISPATCH Dispatch failed
M-STA-003 STA_SAGA Saga execution failed
M-STA-004 STA_COMBINE Combined reducer failed
M-APP-001 APP_LIFECYCLE App lifecycle failure
M-APP-002 APP_RENDER Render failure
M-APP-003 APP_TEMPLATE Template setup or lookup failure
M-FRM-001 FRM_VALIDATION Form validation failure
M-FRM-002 FRM_FIELD Invalid form field configuration
M-FRM-003 FRM_SUBMIT Form submit failure
M-FLW-001 FLW_TRANSITION Invalid or missing transition
M-FLW-002 FLW_SCREEN Invalid screen
M-FLW-003 FLW_DUPLICATE Duplicate flow entry
M-DEV-001 DEV_WATCH File watching failure
M-DEV-002 DEV_RELOAD Hot reload failure
M-CFG-001 CFG_PARSE Config parse failure
M-CFG-002 CFG_MERGE Config merge failure
M-CFG-003 CFG_VALIDATE Config validation failure
M-CFG-004 CFG_MISSING Required config value or file missing
M-PIP-001 PIP_PHASE Pipeline phase failure
M-PIP-002 PIP_TIMEOUT Pipeline timeout
M-PIP-003 PIP_DEPENDENCY Pipeline dependency failure
M-PLG-001 PLG_LOAD Plugin load failure
M-PLG-002 PLG_HOOK Plugin hook failure
M-CMD-001 CMD_NOT_FOUND Command was not found
M-CMD-002 CMD_AMBIGUOUS Command resolution was ambiguous

Terminal Formatting

MiloError.format_compact()keeps terminal output short and actionable:

M-FRM-001 `environment`: Environment is required
  constraint: {'minLength': 1}
  hint: Provide a non-empty environment name.

Use format_error(exc)when boundary code needs to render arbitrary exceptions. It usesformat_compact() when available and falls back to TypeError: ... style text for plain exceptions.

MCP Tool Errors

MCPtools/callerrors are returned as tool results, not JSON-RPC protocol errors:

{
  "content": [{"type": "text", "text": "Error: ..."}],
  "isError": true,
  "errorData": {
    "tool": "deploy",
    "errorCode": "M-FRM-001",
    "type": "MiloError",
    "argument": "environment",
    "constraint": {"minLength": 1},
    "example": "x",
    "suggestion": "Provide a non-empty environment name.",
    "schema": {"type": "object", "properties": {"environment": {"type": "string"}}}
  }
}

For plain Python TypeErrorfrom a missing required argument, Milo parses the argument name and adds:

{
  "argument": "name",
  "reason": "missing_required_argument",
  "suggestion": "Provide 'name'."
}

Agents should use errorData.argument, errorData.constraint, errorData.reason, and errorData.schemato repair the next call. The content[0].textfield is for humans.

JSON-RPC Protocol Errors

The stdin/stdout MCP server maps exceptions that escape method dispatch to JSON-RPC error codes:

Condition JSON-RPC code
Invalid JSON -32700parse error
Validation/config/form/inputMiloError -32602invalid params
CMD_NOT_FOUND -32601method not found
Other exceptions -32603internal error

Most command handler failures should stay insidetools/call as isError tool results witherrorData.

Raising Repairable Errors

Prefer structured errors at validation boundaries:

from milo import ErrorCode, MiloError


def require_env(environment: str) -> None:
    if not environment:
        raise MiloError(
            ErrorCode.FRM_VALIDATION,
            "Environment is required",
            argument="environment",
            constraint={"minLength": 1},
            suggestion="Pass --environment staging or another non-empty value.",
        )

Do not raise broad plain exceptions when an agent could repair the call from a specific argument and constraint.