{"record":{"id":"7efe6847d6bd0890","repo":"Hmbown/CodeWhale","slug":"choose-one-runtime-thread-id","errorCode":null,"errorMessage":"Choose one Runtime --thread ID.","messagePattern":"Choose one Runtime --thread ID\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/scripts/lib/pet-runtime.mjs","lineNumber":14,"sourceCode":"import { setTimeout as delay } from 'node:timers/promises';\nimport { privacyEvent, redact } from '../../dist/core/ingest.js';\nimport { CodewhaleRuntimeTrace, isCodewhaleRuntimeRecord, observeRuntimeRequests } from '../../dist/core/codewhale.js';\n\n/** A read-only transport for the existing Runtime journal. All event meaning\n * remains in Whalesong's importer and canonical pet bucketer. No raw journal,\n * prompt, tool argument or bearer token is written into the pet recording. */\nexport async function followRuntime({ baseUrl, threadId, token, report = () => {} }) {\n  const url = new URL(baseUrl);\n  if (url.protocol !== 'http:' || !['127.0.0.1', '[::1]'].includes(url.hostname)\n    || url.username || url.password || url.pathname !== '/' || url.search || url.hash)\n    throw new Error('Pet Runtime input requires a plain HTTP loopback IP origin, without credentials or a path.');\n  if (typeof threadId !== 'string' || !threadId.trim() || threadId.length > 512)\n    throw new Error('Choose one Runtime --thread ID.');\n  let sdk;\n  try { sdk = await import('@codewhale/runtime-sdk'); }\n  catch { sdk = await import('../../../npm/runtime-sdk/index.js'); }\n  if (typeof sdk.CodeWhaleRuntimeClient.prototype.threadEvents !== 'function')\n    throw new Error('The local Runtime SDK needs threadEvents support.');\n  const client = new sdk.CodeWhaleRuntimeClient({ baseUrl: url.href, token });\n  const shutdown = new AbortController();\n  const trace = new CodewhaleRuntimeTrace('Codewhale Runtime', 250_000,\n    event => privacyEvent(event, 'metadata'), 64 * 1024 * 1024);\n  let cursor = 0, revision = 0, connected = false, fatal = false;\n  const done = (async () => {\n    let backoff = 250;\n    while (!shutdown.signal.aborted && !fatal) {\n      // Fifteen-second server heartbeats make a silent, half-open connection\n      // distinguishable from an idle journal. The timeout is driver time only.\n      const attempt = new AbortController();\n      const signal = AbortSignal.any([shutdown.signal, attempt.signal]);\n      let idleTimer;","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/scripts/lib/pet-runtime.mjs#L1-L32","documentation":"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.","triggerScenarios":"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).","commonSituations":"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`.","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."],"exampleFix":"// before\nfollowRuntime({ baseUrl: 'http://127.0.0.1:8080', threadId: process.env.THREAD_ID }); // undefined\n// after\nconst threadId = process.env.THREAD_ID;\nif (!threadId?.trim()) throw new Error('Set THREAD_ID (--thread) before following a Runtime.');\nfollowRuntime({ baseUrl: 'http://127.0.0.1:8080', threadId });","handlingStrategy":"validation","validationCode":"const threadId = rawId?.trim();\nif (typeof threadId !== 'string' || !threadId || threadId.length > 512)\n  throw new Error('threadId must be a non-empty string of at most 512 characters');","typeGuard":"function isValidThreadId(v) {\n  return typeof v === 'string' && v.trim().length > 0 && v.length <= 512;\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["validation","cli","arguments"],"backgroundTag":"missing-required-argument","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}