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

  1. Print the failing record to see whether it fails the shape check, thread_id match, or seq check.
  2. Verify the --thread ID exactly matches the thread_id embedded in the journal records (case/ID canonicalization).
  3. Align Runtime server and client versions so the record schema matches isCodewhaleRuntimeRecord's expectation.
  4. 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

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


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(fatal

View on GitHub (pinned to 433685b202)