JuliusBrussee/caveman · error · Error
cave_harness_signal_invalid
cave_harness_signal_invalid
Error message
cave_harness_signal_invalid
What it means
`snapshotRequest` validates an optional `AbortSignal`: if `request.signal` is defined, it must expose `aborted: boolean` and `addEventListener: function`. Anything else (a plain object masquerading as a signal, a stubbed signal) throws `cave_harness_signal_invalid`. Note: in the current source this throw happens inside the function's `try` block, so the enclosing `catch` rewrites it into `cave_harness_request_invalid` — callers will in practice observe the request_invalid code; the signal_invalid condition is still the underlying cause to fix.
Source
Thrown at packages/agent/src/adapters.ts:626
function deepFreeze<T>(value: T): T {
if (value !== null && typeof value === "object") {
Object.freeze(value);
for (const child of Object.values(value)) deepFreeze(child);
}
return value;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function snapshotRequest(request: HarnessRequest): Readonly<HarnessRequest> {
try {
const { signal, ...serializable } = request;
const snapshot = deepFreeze(structuredClone(serializable));
if (signal === undefined) return snapshot;
if (typeof signal.aborted !== "boolean" || typeof signal.addEventListener !== "function") {
throw new Error("cave_harness_signal_invalid");
}
return Object.freeze({ ...snapshot, signal });
} catch {
throw new Error("cave_harness_request_invalid");
}
}
function snapshotExecution(value: HarnessExecution): Readonly<HarnessExecution> {
if (!isRecord(value) ||
!Array.isArray(value.evaluatedTransformIDs) ||
!Array.isArray(value.appliedTransformIDs)) {
throw new Error("cave_harness_incomplete_evidence");
}
return deepFreeze({
terminal: value.terminal,
text: value.text,
provider: value.provider,
model: value.model,View on GitHub (pinned to 27d5a3981a)
Solutions
- Pass a real `AbortSignal` from `new AbortController().signal`, or omit `signal` entirely if cancellation is not needed.
- In tests, use Node's built-in `AbortController` rather than hand-rolled signal stubs.
- If you have a custom cancellation source, bridge it: create an `AbortController`, forward your events, and pass its signal.
Example fix
// before
await adapter.run({ ..., signal: { aborted: false } as AbortSignal });
// after
const controller = new AbortController();
await adapter.run({ ..., signal: controller.signal }); Defensive patterns
Strategy: type-guard
Validate before calling
function isAbortSignalLike(v: unknown): v is AbortSignal {
return v === undefined || (
typeof v === "object" && v !== null &&
typeof (v as AbortSignal).aborted === "boolean" &&
typeof (v as AbortSignal).addEventListener === "function"
);
}
if (!isAbortSignalLike(request.signal)) throw new Error("pass a real AbortSignal or omit it"); Type guard
function isAbortSignalLike(v: unknown): v is AbortSignal {
return typeof v === "object" && v !== null &&
typeof (v as AbortSignal).aborted === "boolean" &&
typeof (v as AbortSignal).addEventListener === "function";
} Try / catch
try {
await adapter.run(request);
} catch (err) {
// note: current source rewrites this to cave_harness_request_invalid (error 29);
// treat that code with a signal present as this condition
if (err instanceof Error && err.message === "cave_harness_request_invalid" && request.signal) {
// replace the fake signal with new AbortController().signal and retry
}
} Prevention
- Always use the platform AbortController; never hand-roll signal objects.
- Omit signal when cancellation is not needed rather than passing a placeholder.
- Bridge custom cancellation sources through a real AbortController.
When it happens
Trigger: Setting `request.signal` to a mock object like `{ aborted: false }` without `addEventListener`; a custom abort implementation missing the DOM-standard methods; a signal polyfill that only partially implements the interface.
Common situations: Test doubles replacing `AbortController` with minimal fakes; older runtimes without full `AbortSignal`; passing a `EventTarget`-like cancellation token from a different library instead of a standard `AbortSignal`.
Related errors
- cave_harness_model_invalid
- cave_harness_model_identity_missing
- cave_harness_wire_contract_invalid
- option not found
- cave_harness_adapter_version_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/0f3187157d91a122.
Report an issue: GitHub.