Skip to content

Stable exit codes, structured errors, idempotency #12

Description

@apgiorgi

Motivation

Agents branch on exit codes and parse stderr to decide whether to retry, escalate, or give up. If our exit codes are "0 or 1" and our error messages are free-text English, agents waste tokens guessing. This bucket defines the contract that everything in agent-mode (#10) hangs off.

Spec

Exit codes (stable contract — bumping them is a breaking change)

Propose the standard sysexits-style range, mapped to our domain:

Code Meaning
0 Success
1 Generic failure (fallback)
2 Usage error (bad flags, unknown command)
10 Authentication failure
11 Authorization / permission denied
20 Resource not found
21 Resource conflict / already exists
30 Validation error (request well-formed, semantically wrong)
40 API server error (5xx upstream)
41 Network / connectivity error
50 Rate-limited (with Retry-After hint in JSON body)

Final mapping TBD; the principle is stable, documented, and finite.

Structured error envelope

When agent-mode (#10) is on, errors go to stderr as a single JSON object:

{
  "error": {
    "code": "AUTH_EXPIRED",
    "message": "Token expired at 2026-05-28T10:00:00Z",
    "hint": "Run: dci auth login",
    "retryable": false,
    "http_status": 401,
    "request_id": "abc123"
  }
}

Stable code strings are the contract; message is for humans.

Idempotency

  • All mutating operations accept (and document) an idempotency key
  • Same key + same payload = same result, no double-execution
  • Same key + different payload = explicit conflict error (code 21)

No interactive prompts in agent mode

  • No "are you sure? (y/n)" — fail closed with a clear error code instead
  • See Destructive-operation flagging #17 (destructive-op flagging) for the explicit-confirmation mechanism

Definitive empty states

  • Empty list → {"count": 0, "results": []}, exit 0
  • Never silent stdout; never ambiguous "no output = success or zero?"

References

Open questions

  • Does the DCI API already return stable error codes we can pass through?
  • Is idempotency-key support feasible API-side, or do we approximate client-side?
  • What's our policy for evolving the exit code map (deprecation window, etc.)?

Acceptance criteria

  • Documented exit-code table, treated as a stable contract
  • Structured JSON error envelope in agent mode (stable code strings)
  • All mutating ops document idempotency behavior
  • Zero interactive prompts in agent mode
  • Empty results emit explicit {"count":0,"results":[]} and exit 0

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions