{"record":{"id":"90d4637df744bc9a","repo":"shareAI-lab/learn-claude-code","slug":"invalid-resume-journal-record-at-line-line-number","errorCode":null,"errorMessage":"invalid resume journal record at line {line_number}","messagePattern":"invalid resume journal record at line (.+?)","errorType":"validation","errorClass":"WorkflowInputError","httpStatus":null,"severity":"error","filePath":"s16_workflow_runtime/code.py","lineNumber":344,"sourceCode":"        store = STORE if store is None else store\n        store.mkdir(parents=True, exist_ok=True)\n        self.path = store / f\"{run_id}.journal.jsonl\"\n        self.resume = resume\n        self.cache = {}\n        if resume:\n            if not self.path.exists():\n                raise WorkflowInputError(f\"resume journal not found for {run_id}\")\n            for line_number, line in enumerate(self.path.read_text().splitlines(), start=1):\n                try:\n                    rec = json.loads(line)\n                    if (\n                        not isinstance(rec, dict)\n                        or not isinstance(rec.get(\"key\"), str)\n                        or \"value\" not in rec\n                    ):\n                        raise ValueError(\"expected key/value record\")\n                except (json.JSONDecodeError, ValueError) as exc:\n                    raise WorkflowInputError(\n                        f\"invalid resume journal record at line {line_number}\"\n                    ) from exc\n                self.cache[rec[\"key\"]] = rec[\"value\"]\n            self._f = self.path.open(\"a\")\n        else:\n            self._f = self.path.open(\"w\")             # fresh run truncates\n\n    def key(self, kind, label, prompt, schema):\n        # Deterministic semantic key, independent of concurrency order, so a\n        # parallel/pipeline call gets the same key on resume.\n        basis = f\"{kind}|{label}|{prompt}|{json.dumps(schema, sort_keys=True)}\"\n        return f\"{kind}-{_stable_hash(basis) % 10**10:010d}\"\n\n    def cached(self, key):\n        return self.cache.get(key, MISS)\n\n    def record(self, key, value):\n        self._f.write(json.dumps({\"key\": key, \"value\": value}) + \"\\n\")","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s16_workflow_runtime/code.py#L326-L362","documentation":"On resume, every line of the journal must be a JSON object with a string 'key' and a 'value' field. The first line violating this — unparseable JSON, a non-dict, or a missing/mistyped key — raises WorkflowInputError with that 1-based line number (chained from the underlying JSONDecodeError/ValueError). The journal is append-only JSONL written by the runtime, so corruption is external.","triggerScenarios":"A partially written last line after a crash mid-append (power loss, SIGKILL during write). Manual edits to the .jsonl file. Disk corruption or an editor that re-saved with different encoding/line endings merging records. Concurrent writers to the same journal without the run lock.","commonSituations":"Hard-killed processes leaving a torn final line. Developers hand-trimming journals to 'clean' bad entries. Sync tools (Dropbox-style) mangling concurrent writes.","solutions":["Inspect the reported line: if it is a torn trailing line, delete that single line — earlier records still replay","Restore the journal from backup if corruption is not just the tail","If entries are unusable, start a fresh run rather than hand-editing records"],"exampleFix":"# before: journal ends with torn line\n{\"key\": \"agent-0001234567\", \"value\": {\"summary\": \"...\"}}\n{\"key\": \"agent-000987654\n\n# after: remove the incomplete trailing line\n{\"key\": \"agent-0001234567\", \"value\": {\"summary\": \"...\"}}","handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\ndef journal_is_intact(path: Path) -> bool:\n    for line in path.read_text().splitlines():\n        try:\n            rec = json.loads(line)\n        except json.JSONDecodeError:\n            return False\n        if not isinstance(rec, dict) or not isinstance(rec.get(\"key\"), str) or \"value\" not in rec:\n            return False\n    return True\n\nassert journal_is_intact(STORE / f\"{run_id}.journal.jsonl\")","typeGuard":null,"tryCatchPattern":"try:\n    result = resume(workflow, run_id=run_id)\nexcept WorkflowInputError as exc:\n    if \"invalid resume journal record at line\" in str(exc):\n        line_no = int(str(exc).rsplit(\"line \", 1)[1].rstrip(\")\"))\n        path = STORE / f\"{run_id}.journal.jsonl\"\n        lines = path.read_text().splitlines()\n        # drop the corrupt (typically torn trailing) line and retry once\n        path.write_text(\"\\n\".join(lines[: line_no - 1]) + \"\\n\")\n        result = resume(workflow, run_id=run_id)\n    else:\n        raise","preventionTips":["Never hand-edit journal files; treat them as opaque append-only logs","Tolerate crashes by dropping only the final torn line, never middle lines","Keep the store off editors/sync tools that rewrite files"],"tags":["workflow","resume","journal","data-corruption","jsonl"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}