shareAI-lab/learn-claude-code · error · WorkflowInputError

invalid resume journal record at line {line_number}

Error message

invalid resume journal record at line {line_number}

What it means

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.

Source

Thrown at s16_workflow_runtime/code.py:344

        store = STORE if store is None else store
        store.mkdir(parents=True, exist_ok=True)
        self.path = store / f"{run_id}.journal.jsonl"
        self.resume = resume
        self.cache = {}
        if resume:
            if not self.path.exists():
                raise WorkflowInputError(f"resume journal not found for {run_id}")
            for line_number, line in enumerate(self.path.read_text().splitlines(), start=1):
                try:
                    rec = json.loads(line)
                    if (
                        not isinstance(rec, dict)
                        or not isinstance(rec.get("key"), str)
                        or "value" not in rec
                    ):
                        raise ValueError("expected key/value record")
                except (json.JSONDecodeError, ValueError) as exc:
                    raise WorkflowInputError(
                        f"invalid resume journal record at line {line_number}"
                    ) from exc
                self.cache[rec["key"]] = rec["value"]
            self._f = self.path.open("a")
        else:
            self._f = self.path.open("w")             # fresh run truncates

    def key(self, kind, label, prompt, schema):
        # Deterministic semantic key, independent of concurrency order, so a
        # parallel/pipeline call gets the same key on resume.
        basis = f"{kind}|{label}|{prompt}|{json.dumps(schema, sort_keys=True)}"
        return f"{kind}-{_stable_hash(basis) % 10**10:010d}"

    def cached(self, key):
        return self.cache.get(key, MISS)

    def record(self, key, value):
        self._f.write(json.dumps({"key": key, "value": value}) + "\n")

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Inspect the reported line: if it is a torn trailing line, delete that single line — earlier records still replay
  2. Restore the journal from backup if corruption is not just the tail
  3. If entries are unusable, start a fresh run rather than hand-editing records

Example fix

# before: journal ends with torn line
{"key": "agent-0001234567", "value": {"summary": "..."}}
{"key": "agent-000987654

# after: remove the incomplete trailing line
{"key": "agent-0001234567", "value": {"summary": "..."}}
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path

def journal_is_intact(path: Path) -> bool:
    for line in path.read_text().splitlines():
        try:
            rec = json.loads(line)
        except json.JSONDecodeError:
            return False
        if not isinstance(rec, dict) or not isinstance(rec.get("key"), str) or "value" not in rec:
            return False
    return True

assert journal_is_intact(STORE / f"{run_id}.journal.jsonl")

Try / catch

try:
    result = resume(workflow, run_id=run_id)
except WorkflowInputError as exc:
    if "invalid resume journal record at line" in str(exc):
        line_no = int(str(exc).rsplit("line ", 1)[1].rstrip(")"))
        path = STORE / f"{run_id}.journal.jsonl"
        lines = path.read_text().splitlines()
        # drop the corrupt (typically torn trailing) line and retry once
        path.write_text("\n".join(lines[: line_no - 1]) + "\n")
        result = resume(workflow, run_id=run_id)
    else:
        raise

Prevention

When it happens

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

Common situations: Hard-killed processes leaving a torn final line. Developers hand-trimming journals to 'clean' bad entries. Sync tools (Dropbox-style) mangling concurrent writes.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/90d4637df744bc9a. Report an issue: GitHub.