Hmbown/CodeWhale · error · Error
The local Runtime SDK needs threadEvents support.
Error message
The local Runtime SDK needs threadEvents support.
What it means
followRuntime imports the Codewhale Runtime SDK (preferentially @codewhale/runtime-sdk, falling back to the local npm/runtime-sdk bundle) and requires CodeWhaleRuntimeClient.prototype.threadEvents to be an async iterator method for live/replay journal streaming. If the resolved SDK module is old or stubbed and lacks threadEvents on the client prototype, the script refuses to proceed rather than fail mid-stream.
Solutions
- Update the @codewhale/runtime-sdk dependency to a version whose CodeWhaleRuntimeClient implements threadEvents (check its prototype in node REPL: typeof sdk.CodeWhaleRuntimeClient.prototype.threadEvents).
- If the local fallback npm/runtime-sdk/index.js is being loaded, sync it to the current SDK build that includes threadEvents.
- Check that no stub/mock/alias (e.g. import maps, symlinked node_modules) is replacing the real SDK module.
- Pin the dependency in package.json/lockfile so the local and installed versions agree.
Example fix
// before (old vendored SDK) npm/runtime-sdk/index.js // client without threadEvents // after npm update @codewhale/runtime-sdk # or re-vendor npm/runtime-sdk/index.js from a build exposing CodeWhaleRuntimeClient.prototype.threadEvents
Defensive patterns
Strategy: validation
Validate before calling
const sdk = await import('@codewhale/runtime-sdk').catch(() => import('../../../npm/runtime-sdk/index.js'));
if (typeof sdk.CodeWhaleRuntimeClient?.prototype?.threadEvents !== 'function')
throw new Error('Runtime SDK lacks threadEvents; upgrade @codewhale/runtime-sdk.'); Type guard
function supportsThreadEvents(sdk) {
return typeof sdk?.CodeWhaleRuntimeClient?.prototype?.threadEvents === 'function';
} Prevention
- Pin and regularly update @codewhale/runtime-sdk in package.json/lockfile.
- Check typeof sdk.CodeWhaleRuntimeClient.prototype.threadEvents === 'function' in a smoke test.
- Keep the local npm/runtime-sdk fallback in sync with the published SDK version.
When it happens
Trigger: Calling followRuntime (used by scripts/pet.mjs --runtime=... --thread=...) when the imported SDK module's CodeWhaleRuntimeClient class has no threadEvents function on its prototype — e.g. an outdated installed @codewhale/runtime-sdk package, or the local ../../../npm/runtime-sdk/index.js fallback exporting an older client without that API.
Common situations: A stale npm-installed @codewhale/runtime-sdk shadowing the repo's local copy; a workspace where npm/runtime-sdk/index.js was vendored from an older release; a mock or alias of the SDK injected via module resolution that only implements connection methods but not threadEvents.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/382555a85cd1c6d6.
Report an issue: GitHub.
Appendix: source
Thrown at pet/scripts/lib/pet-runtime.mjs:19
import { setTimeout as delay } from 'node:timers/promises';
import { privacyEvent, redact } from '../../dist/core/ingest.js';
import { CodewhaleRuntimeTrace, isCodewhaleRuntimeRecord, observeRuntimeRequests } from '../../dist/core/codewhale.js';
/** A read-only transport for the existing Runtime journal. All event meaning
* remains in Whalesong's importer and canonical pet bucketer. No raw journal,
* prompt, tool argument or bearer token is written into the pet recording. */
export async function followRuntime({ baseUrl, threadId, token, report = () => {} }) {
const url = new URL(baseUrl);
if (url.protocol !== 'http:' || !['127.0.0.1', '[::1]'].includes(url.hostname)
|| url.username || url.password || url.pathname !== '/' || url.search || url.hash)
throw new Error('Pet Runtime input requires a plain HTTP loopback IP origin, without credentials or a path.');
if (typeof threadId !== 'string' || !threadId.trim() || threadId.length > 512)
throw new Error('Choose one Runtime --thread ID.');
let sdk;
try { sdk = await import('@codewhale/runtime-sdk'); }
catch { sdk = await import('../../../npm/runtime-sdk/index.js'); }
if (typeof sdk.CodeWhaleRuntimeClient.prototype.threadEvents !== 'function')
throw new Error('The local Runtime SDK needs threadEvents support.');
const client = new sdk.CodeWhaleRuntimeClient({ baseUrl: url.href, token });
const shutdown = new AbortController();
const trace = new CodewhaleRuntimeTrace('Codewhale Runtime', 250_000,
event => privacyEvent(event, 'metadata'), 64 * 1024 * 1024);
let cursor = 0, revision = 0, connected = false, fatal = false;
const done = (async () => {
let backoff = 250;
while (!shutdown.signal.aborted && !fatal) {
// Fifteen-second server heartbeats make a silent, half-open connection
// distinguishable from an idle journal. The timeout is driver time only.
const attempt = new AbortController();
const signal = AbortSignal.any([shutdown.signal, attempt.signal]);
let idleTimer;
const refresh = () => { clearTimeout(idleTimer); idleTimer = setTimeout(() => attempt.abort(), 45_000); };
refresh();
const fetchImpl = client.fetchImpl;
client.fetchImpl = async (input, init) => {
const response = await fetchImpl(input, init);View on GitHub (pinned to 433685b202)