tinyhumansai/openhuman · error · Error

no thread id in create_new envelope: ${JSON.stringify(create

Error message

no thread id in create_new envelope: ${JSON.stringify(created).slice(0, 200)}

What it means

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.

Source

Thrown at scripts/debug/agent-prepare-context-audit.mjs:480

// Seed a *prior* conversation the scout can find via `transcript_search`. Plants
// a distinctive canary fact so a transcript-recall case can prove the scout read
// past chat (the summary should echo the canary). Returns { threadId, canary,
// query } or null on failure (seeding is best-effort — the audit still runs).
async function seedTranscript(opts) {
  const canary = `deploy-canary-${randomBytes(4).toString("hex")}`;
  const nowIso = new Date().toISOString();
  try {
    // create_new auto-generates the thread id; pull it from the envelope.
    const created = await rpc(
      opts.coreUrl,
      opts.token,
      "openhuman.threads_create_new",
      { labels: ["apc-audit-seed"] },
      opts.rpcTimeoutMs,
    );
    const threadId = created?.data?.id || created?.id;
    if (!threadId)
      throw new Error(
        `no thread id in create_new envelope: ${JSON.stringify(created).slice(0, 200)}`,
      );
    const message = {
      id: `seed-${randomBytes(4).toString("hex")}`,
      content: `Earlier I told you the staging deploy passphrase is "${canary}". Please remember it for later.`,
      type: "text",
      extraMetadata: {},
      sender: "user",
      createdAt: nowIso,
    };
    await rpc(
      opts.coreUrl,
      opts.token,
      "openhuman.threads_message_append",
      { thread_id: threadId, message },
      opts.rpcTimeoutMs,
    );
    return {

View on GitHub (pinned to a221052e0d)

Solutions

  1. Read the dumped envelope in the error — it shows exactly where the id actually lives, telling you how stale the core is
  2. 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
  3. If you don't need the transcript-recall case, run with --no-seed-transcript to skip the seeding RPC entirely
  4. If the envelope changed intentionally, update the extraction line (created?.data?.id || created?.id) to match the new shape

Example fix

# before
$ node scripts/debug/agent-prepare-context-audit.mjs
Error: no thread id in create_new envelope: {"data":{"thread":{"id":"th_123"}}}

# after (run against a core built from this branch, or skip seeding)
$ node scripts/debug/agent-prepare-context-audit.mjs --spawn-core
# or
$ node scripts/debug/agent-prepare-context-audit.mjs --no-seed-transcript
Defensive patterns

Strategy: type-guard

Validate before calling

const created = await rpc(coreUrl, token, "openhuman.threads_create_new", { labels: ["apc-audit-seed"] }, timeout);
if (!extractThreadId(created)) {
  console.error("unexpected envelope:", JSON.stringify(created).slice(0, 400));
  throw new Error("threads_create_new envelope drifted from {data.id}|{id} — core/script version mismatch");
}

Type guard

function extractThreadId(envelope) {
  if (envelope && typeof envelope === "object") {
    const id = envelope?.data?.id ?? envelope?.id;
    if (typeof id === "string" && id.length > 0) return id;
  }
  return null;
}

Try / catch

try {
  threadId = extractThreadId(await rpc(coreUrl, token, "openhuman.threads_create_new", params));
} catch (e) {
  if (/no thread id in create_new envelope/.test(e.message)) {
    // version skew: dump the envelope, skip the canary case rather than abort the whole audit
    console.warn(`${e.message} — continuing with --no-seed-transcript semantics`);
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/f4d92e07e32eab29. Report an issue: GitHub.