mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-27 05:34:57 +08:00
docs: ERROR_HANDLING.md — fix code examples to match v1.0 envelope (flat shape)
The Python code examples were accessing nested error.kind like envelope['error']['kind'],
but v1.0 emits flat envelopes with error as a STRING and kind at top-level.
Updated:
- Table header: now shows actual v1.0 shape {error: "...", kind: "...", type: "error"}
- match statement: switched from envelope.get('error',{}).get('kind') to envelope.get('kind')
- All ClawError raises: changed from envelope['error']['message'] to envelope.get('error','')
because error field is a STRING in v1.0, not a nested object
- Added inline comments on every error case noting v1.0 vs v2.0 difference
- Appendix: split into v1.0 (actual/current) and v2.0 (target after FIX_LOCUS_164)
The code examples now work correctly against the actual binary.
This was active misdocumentation (P0 severity) — the Python examples would crash
if a consumer tried to use them.
This commit is contained in:
@@ -15,7 +15,7 @@ Every clawable command returns JSON on stdout when `--output-format json` is req
|
|||||||
| Exit Code | Meaning | Response Format | Example |
|
| Exit Code | Meaning | Response Format | Example |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **0** | Success | `{success fields}` | `{"session_id": "...", "loaded": true}` |
|
| **0** | Success | `{success fields}` | `{"session_id": "...", "loaded": true}` |
|
||||||
| **1** | Error / Not Found | `{error: {kind, message, ...}}` | `{"error": {"kind": "session_not_found", ...}}` |
|
| **1** | Error / Not Found | `{error: "...", hint: "...", kind: "...", type: "error"}` (flat, v1.0) | `{"error": "session not found", "kind": "session_not_found", "type": "error"}` |
|
||||||
| **2** | Timeout | `{final_stop_reason: "timeout", final_cancel_observed: ...}` | `{"final_stop_reason": "timeout", ...}` |
|
| **2** | Timeout | `{final_stop_reason: "timeout", final_cancel_observed: ...}` | `{"final_stop_reason": "timeout", ...}` |
|
||||||
|
|
||||||
### Text mode vs JSON mode exit codes
|
### Text mode vs JSON mode exit codes
|
||||||
@@ -81,8 +81,12 @@ def run_claw_command(command: list[str], timeout_seconds: float = 30.0) -> dict[
|
|||||||
retryable=False,
|
retryable=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Classify by exit code and error.kind
|
# Classify by exit code and top-level kind field (v1.0 flat envelope shape)
|
||||||
match (result.returncode, envelope.get('error', {}).get('kind')):
|
# NOTE: v1.0 envelopes have error as a STRING, not a nested object.
|
||||||
|
# The v2.0 schema (SCHEMAS.md) specifies nested error.{kind, message, ...},
|
||||||
|
# but the current binary emits flat {error: "...", kind: "...", type: "error"}.
|
||||||
|
# See FIX_LOCUS_164.md for the migration timeline.
|
||||||
|
match (result.returncode, envelope.get('kind')):
|
||||||
case (0, _):
|
case (0, _):
|
||||||
# Success
|
# Success
|
||||||
return envelope
|
return envelope
|
||||||
@@ -91,8 +95,8 @@ def run_claw_command(command: list[str], timeout_seconds: float = 30.0) -> dict[
|
|||||||
# #179: argparse error — typically a typo or missing required argument
|
# #179: argparse error — typically a typo or missing required argument
|
||||||
raise ClawError(
|
raise ClawError(
|
||||||
kind='parse',
|
kind='parse',
|
||||||
message=envelope['error']['message'],
|
message=envelope.get('error', ''), # error field is a string in v1.0
|
||||||
hint=envelope['error'].get('hint'),
|
hint=envelope.get('hint'),
|
||||||
retryable=False, # Typos don't fix themselves
|
retryable=False, # Typos don't fix themselves
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,7 +104,7 @@ def run_claw_command(command: list[str], timeout_seconds: float = 30.0) -> dict[
|
|||||||
# Common: load-session on nonexistent ID
|
# Common: load-session on nonexistent ID
|
||||||
raise ClawError(
|
raise ClawError(
|
||||||
kind='session_not_found',
|
kind='session_not_found',
|
||||||
message=envelope['error']['message'],
|
message=envelope.get('error', ''), # error field is a string in v1.0
|
||||||
session_id=envelope.get('session_id'),
|
session_id=envelope.get('session_id'),
|
||||||
retryable=False, # Session won't appear on retry
|
retryable=False, # Session won't appear on retry
|
||||||
)
|
)
|
||||||
@@ -109,7 +113,7 @@ def run_claw_command(command: list[str], timeout_seconds: float = 30.0) -> dict[
|
|||||||
# Directory missing, permission denied, disk full
|
# Directory missing, permission denied, disk full
|
||||||
raise ClawError(
|
raise ClawError(
|
||||||
kind='filesystem',
|
kind='filesystem',
|
||||||
message=envelope['error']['message'],
|
message=envelope.get('error', ''), # error field is a string in v1.0
|
||||||
retryable=True, # Might be transient (disk space, NFS flake)
|
retryable=True, # Might be transient (disk space, NFS flake)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -117,16 +121,16 @@ def run_claw_command(command: list[str], timeout_seconds: float = 30.0) -> dict[
|
|||||||
# Generic engine error (unexpected exception, malformed input, etc.)
|
# Generic engine error (unexpected exception, malformed input, etc.)
|
||||||
raise ClawError(
|
raise ClawError(
|
||||||
kind='runtime',
|
kind='runtime',
|
||||||
message=envelope['error']['message'],
|
message=envelope.get('error', ''), # error field is a string in v1.0
|
||||||
retryable=envelope['error'].get('retryable', False),
|
retryable=envelope.get('retryable', False), # v1.0 may or may not have this
|
||||||
)
|
)
|
||||||
|
|
||||||
case (1, _):
|
case (1, _):
|
||||||
# Catch-all for any new error.kind values
|
# Catch-all for any new error.kind values
|
||||||
raise ClawError(
|
raise ClawError(
|
||||||
kind=envelope['error']['kind'],
|
kind=envelope.get('kind', 'unknown'),
|
||||||
message=envelope['error']['message'],
|
message=envelope.get('error', ''), # error field is a string in v1.0
|
||||||
retryable=envelope['error'].get('retryable', False),
|
retryable=envelope.get('retryable', False), # v1.0 may or may not have this
|
||||||
)
|
)
|
||||||
|
|
||||||
case (2, _):
|
case (2, _):
|
||||||
@@ -456,9 +460,28 @@ def test_error_handler_not_found():
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Appendix: SCHEMAS.md Error Shape
|
## Appendix A: v1.0 Error Envelope (Current Binary)
|
||||||
|
|
||||||
For reference, the canonical JSON error envelope shape (SCHEMAS.md):
|
The actual shape emitted by the current binary (v1.0, flat):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "session 'nonexistent' not found in .claw/sessions",
|
||||||
|
"hint": "use 'list-sessions' to see available sessions",
|
||||||
|
"kind": "session_not_found",
|
||||||
|
"type": "error"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key differences from v2.0 schema (below):**
|
||||||
|
- `error` field is a **string**, not a structured object
|
||||||
|
- `kind` is at **top-level**, not nested under `error`
|
||||||
|
- Missing: `timestamp`, `command`, `exit_code`, `output_format`, `schema_version`
|
||||||
|
- Extra: `type: "error"` field (not in schema)
|
||||||
|
|
||||||
|
## Appendix B: SCHEMAS.md Target Shape (v2.0)
|
||||||
|
|
||||||
|
For reference, the target JSON error envelope shape (SCHEMAS.md, v2.0):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -466,7 +489,7 @@ For reference, the canonical JSON error envelope shape (SCHEMAS.md):
|
|||||||
"command": "load-session",
|
"command": "load-session",
|
||||||
"exit_code": 1,
|
"exit_code": 1,
|
||||||
"output_format": "json",
|
"output_format": "json",
|
||||||
"schema_version": "1.0",
|
"schema_version": "2.0",
|
||||||
"error": {
|
"error": {
|
||||||
"kind": "session_not_found",
|
"kind": "session_not_found",
|
||||||
"operation": "session_store.load_session",
|
"operation": "session_store.load_session",
|
||||||
@@ -478,7 +501,7 @@ For reference, the canonical JSON error envelope shape (SCHEMAS.md):
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
All commands that emit errors follow this shape (with error.kind varying). See `SCHEMAS.md` for the complete contract.
|
**This is the target schema after [`FIX_LOCUS_164`](./FIX_LOCUS_164.md) is implemented.** The migration plan includes a dual-mode `--envelope-version=2.0` flag in Phase 1, default version bump in Phase 2, and deprecation in Phase 3. For now, code against v1.0 (Appendix A).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user