Hmbown/CodeWhale · error · Error
Invalid Runtime envelope.
Error message
Invalid Runtime envelope.
What it means
Every non-progress record from client.threadEvents() must pass isCodewhaleRuntimeRecord(), carry the matching thread_id, and have a non-negative safe-integer seq. This is the envelope integrity gate: a malformed, foreign-thread, or incorrectly-sequenced record means the journal stream is corrupt or the server is speaking a different protocol version.
Solutions
- Print the failing record to see whether it fails the shape check, thread_id match, or seq check.
- Verify the --thread ID exactly matches the thread_id embedded in the journal records (case/ID canonicalization).
- Align Runtime server and client versions so the record schema matches isCodewhaleRuntimeRecord's expectation.
- Check for intermediaries (proxies, log tailers) that could rewrite or truncate records; connect directly to the Runtime endpoint.
Example fix
// before: opaque throw
if (!isCodewhaleRuntimeRecord(record) || record.thread_id !== threadId || !Number.isSafeInteger(record.seq) || record.seq < 0)
throw new Error('Invalid Runtime envelope.');
// after: localized diagnosis
if (!isCodewhaleRuntimeRecord(record) || record.thread_id !== threadId || !Number.isSafeInteger(record.seq) || record.seq < 0) {
console.error('envelope', JSON.stringify(record)?.slice(0, 500), 'want thread', threadId);
throw new Error('Invalid Runtime envelope.');
} Defensive patterns
Strategy: validation
Validate before calling
function validEnvelope(record, threadId) {
return isCodewhaleRuntimeRecord(record) &&
record.thread_id === threadId &&
Number.isSafeInteger(record.seq) && record.seq >= 0;
}
// skip records failing validEnvelope after logging them Type guard
const isCodewhaleRuntimeRecord = (r) => r != null && typeof r === 'object' && typeof r.thread_id === 'string' && typeof r.event === 'string' && Number.isSafeInteger(r.seq);
Try / catch
try {
await followRuntime({ baseUrl, threadId });
} catch (err) {
if (err.message === 'Invalid Runtime envelope.') {
console.error('Check server/client protocol versions and thread ID canonicalization');
}
throw err;
} Prevention
- Use the exact thread ID the server reports (no case or prefix changes).
- Connect directly to the Runtime endpoint; avoid rewriting proxies.
- Confirm record schema against isCodewhaleRuntimeRecord after any server upgrade.
When it happens
Trigger: client.threadEvents() yields a record that fails isCodewhaleRuntimeRecord (missing/extra fields, wrong shape), has record.thread_id !== threadId, or record.seq is not a Number.isSafeInteger >= 0 (undefined, negative, float, > 2^53-1).
Common situations: Connecting to a Runtime server of a different protocol version; a transparent proxy mangling JSON payloads; reading a journal file written by another tool; requesting a thread ID whose records contain a different (canonical) thread_id than the one passed on the CLI.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- ${compactRuntimeError(response.status, body)}
- {err}
- Invalid Runtime replay progress.
- PythonRuntime::with_state_path is deprecated — use…
- Runtime predecessor cursor does not match.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/27dc6e7aab56c935.
Report an issue: GitHub.
Appendix: source
Thrown at pet/scripts/lib/pet-runtime.mjs:59
if (response.headers.get('x-codewhale-event-progress') !== '1') {
await response.body.cancel();
const error = new Error('Runtime replay progress is unavailable.'); error.status = 501; throw error;
}
// Cancel the wrapped pipeline too: the original Response can be collected
// while its idle body is still being read through the replacement below.
const body = response.body.pipeThrough(new TransformStream({ transform(chunk, controller) { refresh(); controller.enqueue(chunk); } }), { signal });
return new Response(body, { status: response.status, headers: response.headers });
};
try {
for await (const record of client.threadEvents(threadId, { sinceSeq: cursor, signal, includeProgress: true })) {
if (record?.event === 'stream.progress') {
if (record.thread_id !== threadId || record.seq !== cursor || !['live', 'replaying'].includes(record.state))
throw new Error('Invalid Runtime replay progress.');
connected = record.state === 'live';
continue;
}
if (!isCodewhaleRuntimeRecord(record) || record.thread_id !== threadId || !Number.isSafeInteger(record.seq) || record.seq < 0)
throw new Error('Invalid Runtime envelope.');
if (record.seq <= cursor) continue;
// Sequence numbers belong to Runtime, and need not be consecutive.
// Its predecessor cursor detects loss without inventing a new counter.
if (record.previous_seq !== undefined && record.previous_seq !== cursor)
throw new Error('Runtime predecessor cursor does not match.');
if (record.event !== 'item.delta') {
// The existing importer retains unfinished lifetimes and a recent
// recurrence window, not a second copy of the entire raw journal.
if (revision % 256 === 0) trace.prune(Date.now() - 16_000);
try { trace.append([redact(record)]); }
catch (error) { fatal = true; throw error; }
revision++;
}
cursor = record.seq; backoff = 250;
}
} catch (error) {
if ([400, 401, 403, 404, 405, 501].includes(error.status)) fatal = true;
if (!shutdown.signal.aborted) report(fatalView on GitHub (pinned to 433685b202)