{"record":{"id":"9305e7d08f278b5d","repo":"Hmbown/CodeWhale","slug":"envelope-error","errorCode":null,"errorMessage":"${envelope.error}","messagePattern":"\\$\\{envelope\\.error\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/workflow-js/src/vm.rs","lineNumber":1066,"sourceCode":"  const hostPhase = __workflow_phase;\n  const hostBudgetTotal = __workflow_budget_total;\n  const hostBudgetSpent = __workflow_budget_spent;\n  const hostBudgetRemaining = __workflow_budget_remaining;\n\n  const MAX_ITEMS = __MAX_ITEMS__;\n  const taskErrorText = (err) => String(err && err.message !== undefined ? err.message : err);\n  const isFatalTaskError = (err) => {\n    const text = taskErrorText(err);\n    return text.includes(\"responseSchema\") || text.includes(\"run cancelled\");\n  };\n\n  globalThis.task = async (opts) => {\n    if (opts === null || typeof opts !== \"object\") {\n      throw new TypeError(\"task(): expected an options object\");\n    }\n    const envelope = JSON.parse(await hostTask(JSON.stringify(opts)));\n    if (envelope.error !== undefined) {\n      throw new Error(envelope.error);\n    }\n    return envelope.value;\n  };\n\n  globalThis.parallel = (thunks) => {\n    if (!Array.isArray(thunks)) {\n      throw new TypeError(\"parallel(): expected an array of thunks\");\n    }\n    if (thunks.length > MAX_ITEMS) {\n      throw new Error(\"parallel(): max \" + MAX_ITEMS + \" items per call\");\n    }\n    return Promise.all(thunks.map((thunk) => {\n      try {\n        return Promise.resolve(typeof thunk === \"function\" ? thunk() : thunk).catch((err) => {\n          if (isFatalTaskError(err)) throw err;\n          hostLog(\"parallel(): dropped a failed slot as null: \" + String((err && err.message) || err));\n          return null;\n        });","sourceCodeStart":1048,"sourceCodeEnd":1084,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/workflow-js/src/vm.rs#L1048-L1084","documentation":"Every in-place memory edit (edit/retire/revise) must carry evidence, and normalize_evidence runs that evidence through normalize_note: CR/LF are normalized, lines are trimmed, blank lines dropped, and the result must be non-empty and at most 64 KiB (MAX_NOTE_BYTES). This error means the evidence collapsed to an empty string (it was empty, whitespace-only, or only blank lines) — the size overflow surfaces as a different message. The journal treats an unexplained rewrite of durable context as the exact failure mode it exists to prevent.","triggerScenarios":"Calling store.edit(...) or retire(...) with evidence=\"\", evidence=\"   \", or a string of only newlines/spaces; model tool-calls that fabricate an evidence argument from an empty variable.","commonSituations":"LLM agents calling the memory-edit tool without filling the evidence parameter; scripts templating evidence from an environment variable that is unset; whitespace introduced by trimming upstream.","solutions":["Pass a concrete, non-blank evidence string describing what changed and why, e.g. \"user corrected build command to `pnpm build`\".","If evidence comes from a variable, default it: evidence.is_empty() fallback before calling edit.","Check for the 64 KiB cap too — normalize_evidence shares MAX_NOTE_BYTES with notes, so trim oversized evidence."],"exampleFix":"// before\nstore.edit(scope, id, from, to, \"\")?;\n\n// after\nlet evidence = evidence.trim();\nlet evidence = if evidence.is_empty() { \"manual correction during review\" } else { evidence };\nstore.edit(scope, id, from, to, evidence)?;","handlingStrategy":"validation","validationCode":"fn usable_evidence(evidence: &str) -> bool {\n    let collapsed: String = evidence.lines().map(str::trim).filter(|l| !l.is_empty()).collect();\n    !collapsed.is_empty() && collapsed.len() <= 64 * 1024\n}\n\nif !usable_evidence(&evidence) {\n    anyhow::bail!(\"provide a short non-empty justification for this memory edit\");\n}\nstore.edit(scope, id, from, to, &evidence)?;","typeGuard":"fn is_non_empty_evidence(e: &str) -> bool {\n    e.lines().any(|l| !l.trim().is_empty())\n}","tryCatchPattern":"match store.edit(scope, id, &from, &to, evidence) {\n    Ok(hit) => hit,\n    Err(e) if e.to_string().contains(\"memory edits require non-empty evidence\") =>\n        store.edit(scope, id, &from, &to, \"unspecified manual edit\")?,\n    Err(e) => return Err(e),\n}","preventionTips":["Require a non-empty evidence argument at the tool-call boundary (fail the request before touching the store).","Never template evidence from possibly-unset variables; use a descriptive constant default.","Keep evidence under 64 KiB — it shares MAX_NOTE_BYTES with notes.","Log the evidence alongside the edit so journal audits stay meaningful."],"tags":["rust","native-memory","evidence","validation","journal"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}