{"record":{"id":"f4d92e07e32eab29","repo":"tinyhumansai/openhuman","slug":"no-thread-id-in-create-new-envelope-json-string","errorCode":null,"errorMessage":"no thread id in create_new envelope: ${JSON.stringify(created).slice(0, 200)}","messagePattern":"no thread id in create_new envelope: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/debug/agent-prepare-context-audit.mjs","lineNumber":480,"sourceCode":"// Seed a *prior* conversation the scout can find via `transcript_search`. Plants\n// a distinctive canary fact so a transcript-recall case can prove the scout read\n// past chat (the summary should echo the canary). Returns { threadId, canary,\n// query } or null on failure (seeding is best-effort — the audit still runs).\nasync function seedTranscript(opts) {\n  const canary = `deploy-canary-${randomBytes(4).toString(\"hex\")}`;\n  const nowIso = new Date().toISOString();\n  try {\n    // create_new auto-generates the thread id; pull it from the envelope.\n    const created = await rpc(\n      opts.coreUrl,\n      opts.token,\n      \"openhuman.threads_create_new\",\n      { labels: [\"apc-audit-seed\"] },\n      opts.rpcTimeoutMs,\n    );\n    const threadId = created?.data?.id || created?.id;\n    if (!threadId)\n      throw new Error(\n        `no thread id in create_new envelope: ${JSON.stringify(created).slice(0, 200)}`,\n      );\n    const message = {\n      id: `seed-${randomBytes(4).toString(\"hex\")}`,\n      content: `Earlier I told you the staging deploy passphrase is \"${canary}\". Please remember it for later.`,\n      type: \"text\",\n      extraMetadata: {},\n      sender: \"user\",\n      createdAt: nowIso,\n    };\n    await rpc(\n      opts.coreUrl,\n      opts.token,\n      \"openhuman.threads_message_append\",\n      { thread_id: threadId, message },\n      opts.rpcTimeoutMs,\n    );\n    return {","sourceCodeStart":462,"sourceCodeEnd":498,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/debug/agent-prepare-context-audit.mjs#L462-L498","documentation":"While seeding the prior-chat canary thread, the audit script (scripts/debug/agent-prepare-context-audit.mjs) calls openhuman.threads_create_new and extracts the new thread id from the response envelope via `created?.data?.id || created?.id`. If neither shape is present it throws with a 200-char dump of the envelope. This is a defensive API-contract check: the script accepts two known envelope layouts and refuses to guess further.","triggerScenarios":"The target core's threads_create_new returns a differently-shaped envelope — an older/newer core version that wraps the id elsewhere, a proxy or RPC shim that rewrites the response, or (subtly) a JSON-RPC error that somehow bypassed the earlier body.error guard. Builtin to the --no-seed-transcript-less path only: passing --no-seed-transcript skips this call entirely.","commonSituations":"Running the audit against a stale installed core instead of one built from the current branch (the script header warns about exactly this); envelope refactors in the threads controller; RPC-routing middleware (dev proxies, tokens services) normalizing response bodies.","solutions":["Read the dumped envelope in the error — it shows exactly where the id actually lives, telling you how stale the core is","Rebuild/restart against a core from this branch: cargo build --bin openhuman-core and run with --spawn-core, or point --core-url at the freshly built core","If you don't need the transcript-recall case, run with --no-seed-transcript to skip the seeding RPC entirely","If the envelope changed intentionally, update the extraction line (created?.data?.id || created?.id) to match the new shape"],"exampleFix":"# before\n$ node scripts/debug/agent-prepare-context-audit.mjs\nError: no thread id in create_new envelope: {\"data\":{\"thread\":{\"id\":\"th_123\"}}}\n\n# after (run against a core built from this branch, or skip seeding)\n$ node scripts/debug/agent-prepare-context-audit.mjs --spawn-core\n# or\n$ node scripts/debug/agent-prepare-context-audit.mjs --no-seed-transcript","handlingStrategy":"type-guard","validationCode":"const created = await rpc(coreUrl, token, \"openhuman.threads_create_new\", { labels: [\"apc-audit-seed\"] }, timeout);\nif (!extractThreadId(created)) {\n  console.error(\"unexpected envelope:\", JSON.stringify(created).slice(0, 400));\n  throw new Error(\"threads_create_new envelope drifted from {data.id}|{id} — core/script version mismatch\");\n}","typeGuard":"function extractThreadId(envelope) {\n  if (envelope && typeof envelope === \"object\") {\n    const id = envelope?.data?.id ?? envelope?.id;\n    if (typeof id === \"string\" && id.length > 0) return id;\n  }\n  return null;\n}","tryCatchPattern":"try {\n  threadId = extractThreadId(await rpc(coreUrl, token, \"openhuman.threads_create_new\", params));\n} catch (e) {\n  if (/no thread id in create_new envelope/.test(e.message)) {\n    // version skew: dump the envelope, skip the canary case rather than abort the whole audit\n    console.warn(`${e.message} — continuing with --no-seed-transcript semantics`);\n  } else throw e;\n}","preventionTips":["Pin the core build to the same commit as the audit script — envelope drift is almost always version skew","When a call must match multiple known envelope shapes, extract via a helper that returns null and fail with the dumped payload, like this script does","Use --no-seed-transcript to decouple the transcript-recall case when you only care about the other audit cases"],"tags":["rpc","api-contract","versioning","threads"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}