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.