Hmbown/CodeWhale · error · Error
Choose one Runtime --thread ID.
Error message
Choose one Runtime --thread ID.
What it means
followRuntime() requires a non-empty string threadId of at most 512 characters (after trimming); anything else — missing, empty, whitespace-only, or over-length — throws before connecting. The ID names exactly one Runtime thread to follow.
Solutions
- Supply a non-empty thread ID, e.g. `followRuntime({ baseUrl, threadId: 't-123' })` or pass `--thread <id>` on the CLI.
- Trim the value before passing it; reject empty after trim.
- If the ID exceeds 512 chars, you are passing the wrong value — take the bare ID the Runtime printed.
- Add an early guard in your script so a missing env var fails with a clear message before calling the library.
Example fix
// before
followRuntime({ baseUrl: 'http://127.0.0.1:8080', threadId: process.env.THREAD_ID }); // undefined
// after
const threadId = process.env.THREAD_ID;
if (!threadId?.trim()) throw new Error('Set THREAD_ID (--thread) before following a Runtime.');
followRuntime({ baseUrl: 'http://127.0.0.1:8080', threadId }); Defensive patterns
Strategy: validation
Validate before calling
const threadId = rawId?.trim();
if (typeof threadId !== 'string' || !threadId || threadId.length > 512)
throw new Error('threadId must be a non-empty string of at most 512 characters'); Type guard
function isValidThreadId(v) {
return typeof v === 'string' && v.trim().length > 0 && v.length <= 512;
} Prevention
- Validate the --thread value before calling followRuntime
- Trim whitespace from env/CLI inputs
- If the ID is longer than 512 chars, you grabbed the wrong value
- Fail fast on missing THREAD_ID env vars with a clear message
When it happens
Trigger: Calling followRuntime({ baseUrl }) without threadId; passing an empty string or string of spaces; passing a threadId longer than 512 characters (e.g. a full composite key or pasted JSON instead of the ID).
Common situations: CLI invocation missing `--thread`; environment variable for the thread unset and defaulted to ''; grabbing the wrong value (a URL or session blob) instead of the bare thread ID from `Runtime --thread`.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- --attempts must be an integer from 1 to 20
- --channel, --version and --reason are required
- Command is required
- Invalid pet recording segment limit.
- Provide either --command or --url for `mcp add`.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/7efe6847d6bd0890.
Report an issue: GitHub.
Appendix: source
Thrown at pet/scripts/lib/pet-runtime.mjs:14
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;View on GitHub (pinned to 433685b202)