{"record":{"id":"ba2b3ddcc018b8aa","repo":"tinyhumansai/openhuman","slug":"empty-transcript","errorCode":null,"errorMessage":"empty transcript","messagePattern":"empty transcript","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/debug/agent-prepare-context-audit.mjs","lineNumber":319,"sourceCode":"      entries.map(async (entry) => {\n        const full = path.join(current, entry.name);\n        if (entry.isDirectory()) return walk(full);\n        if (entry.isFile() && entry.name.endsWith(\".jsonl\")) out.push(full);\n      }),\n    );\n  }\n  await walk(dir);\n  return out;\n}\n\nfunction num(value) {\n  return Number.isFinite(Number(value)) ? Number(value) : 0;\n}\n\nasync function readTranscript(file) {\n  const data = await readFile(file, \"utf8\");\n  const lines = data.split(/\\r?\\n/).filter((l) => l.trim());\n  if (lines.length === 0) throw new Error(\"empty transcript\");\n  const meta = JSON.parse(lines[0])._meta || {};\n  const messages = [];\n  for (const line of lines.slice(1)) {\n    try {\n      const m = JSON.parse(line);\n      if (typeof m.role === \"string\") messages.push(m);\n    } catch {\n      // skip malformed line\n    }\n  }\n  return {\n    file,\n    agent: String(meta.agent || \"(unknown)\"),\n    threadId: meta.thread_id || null,\n    isSubagent: path.basename(file).includes(\"__\"),\n    input: num(meta.input_tokens),\n    output: num(meta.output_tokens),\n    cached: num(meta.cached_input_tokens),","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/debug/agent-prepare-context-audit.mjs#L301-L337","documentation":"readTranscript() in scripts/debug/agent-prepare-context-audit.mjs reads a session .jsonl transcript, splits on newlines, filters blank lines, and throws 'empty transcript' when zero lines remain. Session transcripts are written by the core as NDJSON (first line carries _meta, subsequent lines are messages); a file with no content at all means the core created the file but never flushed a line — or the audit is pointing at a file it should not be reading.","triggerScenarios":"The transcript walk (walkJsonl over the workspace's session directories) finds a .jsonl that is zero bytes or whitespace-only — typically a thread whose first turn crashed before the meta line was written, or a file created by a core that was killed mid-write. Reading the very-fresh transcript of a turn that hasn't committed its first line yet can also race this.","commonSituations":"Auditing a workspace that contains aborted/crashed prior sessions; a spawned core dying during the audited turn (its transcript stays empty) and the audit then sweeping it up; workspace pointed at the wrong user dir so unrelated stale files are scanned.","solutions":["Re-run the audit — if the empty file was a crash artifact of a one-off failed turn, the fresh threads will be well-formed","Inspect the empty file's path from the error to identify which thread died, and delete the orphaned zero-byte file","Verify --workspace points at the workspace the audited core actually writes transcripts into","If the core itself is crashing mid-turn, diagnose that first (its stderr / --verbose) — the empty transcript is a symptom, not the cause"],"exampleFix":"// before — the audit's walkJsonl sweeps every *.jsonl, including orphans\nError: empty transcript\n\n// after — remove the orphaned file so the walk skips it\n$ find ~/.openhuman/users/<id>/workspace -name '*.jsonl' -size 0 -delete\n$ node scripts/debug/agent-prepare-context-audit.mjs","handlingStrategy":"validation","validationCode":"import { statSync } from \"node:fs\";\nfunction isReadableTranscript(file) {\n  const st = statSync(file);\n  return st.size > 0; // zero-byte NDJSON can never carry the _meta line\n}\nconst files = (await walkJsonl(dir)).filter(isReadableTranscript);","typeGuard":"async function isWellFormedTranscript(file) {\n  const data = await readFile(file, \"utf8\");\n  const lines = data.split(/\\r?\\n/).filter((l) => l.trim());\n  if (lines.length === 0) return false;\n  try { JSON.parse(lines[0]); return true; } catch { return false; }\n}","tryCatchPattern":"for (const f of transcriptFiles) {\n  try {\n    results.push(await readTranscript(f));\n  } catch (e) {\n    if (/^empty transcript$/.test(e.message)) { console.warn(`skipping orphaned empty transcript ${f}`); continue; }\n    throw e;\n  }\n}","preventionTips":["Filter zero-byte *.jsonl files before analysis — they are crash residue, not data","Scope the workspace: point --workspace at the workspace the audited core writes to, so stale files from other users/runs aren't swept","Treat empty transcripts as a symptom: if they appear for threads the audit just ran, the core is dying mid-turn — debug that"],"tags":["data","transcript","filesystem","audit"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}