domains / / CLINE/CLINE
Explicit Error Handling
Adopt a standard that prevents misleading state, lost root causes, and unhandled async failures—especially in recovery/timeout paths. Rules: 1) Handle both rejection and exceptions in promise chains
Adopt a standard that prevents misleading state, lost root causes, and unhandled async failures—especially in recovery/timeout paths.
Rules: 1) Handle both rejection and exceptions in promise chains
- If you attach via
.then(x, onError), also guard exceptions thrown insidex(setup/stream wiring/attach). Prefer.then(attach).catch(...)when attach can throw.
2) Never leave critical async work unobserved
- Avoid fire-and-forget
.then(...)without a.catch(...)when failure changes UI/state or invariants. Eitherawaitinside the existing try/catch, or attach an explicit catch that logs and/or reports state.
3) Cleanup/cancel timers on the error path
- If you arm timers/observers before an async operation, ensure spawn/connect failures cancel or suppress the observation to avoid false “ran/exited” metrics or late callbacks.
4) Preserve error meaning; don’t silently widen retryability
- Timeouts/session-lost must be classified based on the actual transport/provider semantics. Don’t blanket-mark timeouts as whole-turn retryable; preserve the original timeout phase/reason and diagnostic fields.
5) Never “silently disable” safety
- If protection depends on reading config, distinguish “missing file” vs “corrupt/unreadable file”. If the read fails, either fail closed with an explicit warning or return a user-visible warning while keeping protection behavior consistent with the last known valid value.
6) Route all async stream/write failures into the correct failure handler
- Attach stdin/pipe error listeners (EPIPE/EOF on Windows) so the host doesn’t crash and so the request fails fast with the annotated reason.
Example: replace a fragile promise chain that can throw during attach
const handle = new SandboxedProcess();
void loadMxc()
.then((mxc) => mxc.spawn(/* ... */))
.then((sandboxed) => handle.attach(sandboxed))
.catch((error: unknown) => {
// Ensures both spawn rejection AND attach exceptions are handled.
const reason = error instanceof Error ? error.message : String(error);
handle.fail(new Error(`Sandbox failed: ${reason}`));
});
Example: avoid unhandled commit failures
try {
await this.say("checkpoint_created");
const commitHash = await this.checkpointTracker.commit();
await this.messageStateHandler.updateClineMessage(
/* index */, { lastCheckpointHash: commitHash }
);
} catch (e) {
Logger.warn("checkpoint commit failed", e);
// Preserve previous behavior: show that no hash is available without breaking the run.
}
Example: cancel observation timer on spawn failure
let timer: NodeJS.Timeout | undefined;
try {
timer = setTimeout(report, observationMs);
timer.unref?.();
const child = spawn(/* ... */);
// ...
} catch (e) {
if (timer) clearTimeout(timer);
// Emit only spawn failure; do not emit an exited:false runtime sample.
throw e;
}
Applying these rules consistently will reduce orphaned state, false telemetry, lost attribution (timeouts), and unhandled promise rejections across run/recovery/sandbox/plugin execution flows.