agent's nbformat write failed: invalid JSON, notebook won't open, parse error
Repairs an agent's nbformat write that produced invalid JSON: validate before writing and never hand-assemble notebook dicts. For agent builders.
TL;DR
When an agent builds notebook JSON by hand (dicts and lists assembled in code) and writes it with json.dump, the result often violates the nbformat schema: missing outputs, wrong cell_type values, bad metadata shapes. Build with nbformat.v4.new_notebook(), new_code_cell(), and new_markdown_cell() instead, then nbformat.write(). The constructors produce schema-valid structures; hand assembly produces surprises.
agent's nbformat write failed: invalid JSON, notebook won't open, parse errorUse this when
- A notebook written by an agent will not open or parse.
- The agent constructs notebook dicts manually.
nbformat.validate()fails on the agent's output.
Not for this skill when
- A human's hand edit broke the JSON. Validate and repair.
- nbformat reads fail on an old file. That is a version issue.
Steps
- Validate the broken output:
nbformat.validate(bad_nb). Verify: you see the schema violations. - Replace hand assembly with
nbformat.v4.new_notebook()and thenew_*_cell()constructors. Verify: the constructed notebook validates. - Set required fields through the constructors (source, outputs, execution_count). Verify: no field is missing.
- Write with
nbformat.write(nb, path). Verify: the file opens in Jupyter. - Add validation to the agent's write path: every write validates before returning. Verify: invalid notebooks can never be produced again.
Variant phrasings
agent write invalid notebook json
Short form. Use the constructors.
nbformat write won't open
The symptom phrasing. Validate the output.
newcodecell new_notebook
The fix phrasing. Constructors, not dicts.
Compatibility: nbformat 5.x v4 API. The constructors are the stable way to build notebooks.
Why it happens
The v4 schema has required fields that are easy to forget (code cells need outputs and execution_count, every cell needs metadata), and json.dump happily writes whatever dict you hand it. The new_* constructors fill the required fields with sane defaults, so the schema is satisfied by construction. Hand assembly optimizes for nothing and risks everything.
Edge cases / pitfalls
- Copying a cell dict and mutating it can duplicate cell ids. Generate fresh ids for new cells.
- Outputs need
output_type; a bare dict of text is not an output. Usenew_output(). - Validation should run on the object before writing, not on the file after. Fail before the bad file exists.
- Round-tripping through
json.loads(json.dumps(nb))is a fine extra check for serialization safety.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstN6c52C3uHio72F_xNWnPw