Skip to content

REQ-C-013: Error Responses Include Code and Message

Tier: Command Contract | Priority: P0

Source: §18 Error Message Quality

Addresses: Severity: High / Token Spend: High / Time: Medium / Context: High


Description

Every error response MUST include an error object with: code (string, DOMAIN_NOUN_CONDITION format, machine-readable), message (string, human-readable, complete sentence), and optionally cause (the underlying system error), suggestion (an actionable next step), docs_url (a URL to documentation), and context (an object of relevant key-value pairs). Stack traces MUST NOT appear in error.message or any stdout field; they MUST be sent only to stderr or a log file.

Acceptance Criteria

  • Every non-zero exit code response includes a non-null error object
  • error.code matches the pattern [A-Z][A-Z0-9_]+
  • error.message is a complete human-readable sentence
  • No stack trace text appears anywhere in stdout output
  • error.suggestion is present for all errors the command author classifies as recoverable

Schema

Types: exit-code.md · response-envelope.mderror.code carries an ExitCode value; the full error object is defined in ResponseEnvelope.ErrorDetail


Wire Format

$ tool connect --host db.example.com
{
  "ok": false,
  "data": null,
  "error": {
    "code": "CONNECTION_REFUSED",
    "message": "Cannot connect to database at db.example.com:5432.",
    "cause": "Connection refused (ECONNREFUSED)",
    "suggestion": "Verify the database is running: `tool db status`",
    "docs_url": "https://docs.example.com/errors/CONNECTION_REFUSED",
    "context": {
      "host": "db.example.com",
      "port": 5432,
      "timeout_ms": 5000
    }
  },
  "warnings": [],
  "meta": { "duration_ms": 5003 }
}

Exit code: 12 (UNAVAILABLE). No stack trace in stdout.


Example

A command author maps internal exceptions to structured error objects, providing code, message, and — for recoverable errors — suggestion and context.

register command "connect":
  danger_level: safe
  has_network_io: true
  exit_codes:
    SUCCESS   (0):  description: "Connection established",             retryable: false, side_effects: none
    UNAVAILABLE(12): description: "Remote host could not be reached",  retryable: true,  side_effects: none
    AUTH_REQUIRED(8): description: "Credentials missing or expired",   retryable: true,  side_effects: none

  execute(args):
    try:
      connect(args.host, args.port)
    except ConnectionRefused as e:
      raise StructuredError(
        code="CONNECTION_REFUSED",
        message="Cannot connect to database at {}:{}.".format(args.host, args.port),
        cause=str(e),
        suggestion="Verify the database is running: `tool db status`",
        docs_url="https://docs.example.com/errors/CONNECTION_REFUSED",
        context={"host": args.host, "port": args.port, "timeout_ms": args.timeout_ms},
        exit_code=UNAVAILABLE,
      )
    except AuthError as e:
      raise StructuredError(
        code="AUTH_TOKEN_EXPIRED",
        message="Credentials are missing or expired.",
        suggestion="Run `tool login` to refresh your credentials",
        exit_code=AUTH_REQUIRED,
      )

Requirement Tier Relationship
REQ-F-004 F Provides: ResponseEnvelope.ErrorDetail is the schema type carrying all fields defined here
REQ-F-001 F Provides: ExitCode constants used in error.code for well-known failure categories
REQ-C-001 C Composes: every exit code in error.code must be declared in the command's exit_codes map
REQ-C-014 C Extends: adds retryable and retry_after_ms fields to the error object defined here