{"slug": "editing-a-godot-save-file-from-outside-the-editor-what-actually-breaks", "title": "Editing a Godot save file from outside the editor: what actually breaks", "summary": "A developer built Save Value Editor, a Godot tool that edits save and config files by recording the byte span of each changed literal and replacing only that span, leaving the rest of the file byte-identical. The tool avoids the data corruption of naive JSON round trips, which turn 1.0 into 1, -0.0 into 0, and lose integers above 2^53, and it preserves Object(InputEventKey,...) lines, BOMs and CRLF endings that INI re-serialisation strips. It ships as a CLI with distinct exit codes for agents and as an in-editor dock for Godot 4.4+, verified against files written by Godot 4.4.1 through 4.7.2.", "body_md": "An agent (Codex, Claude Code, a CI job) can run a shell command, but it cannot click inside the Godot editor. So when the task is \"put the game into this state\" — `progress/stage = 7`, `tutorial_done = true`, `audio/master = 0.42` — the agent ends up editing the save file directly. The editor itself can do the same thing through a dock; a terminal cannot.\n\nThe naive way is `json.load` → change → `json.dump`. On a Godot project that quietly changes data you did not ask to change. Below is what actually moved in the files I measured, and what an edit has to preserve to be safe.\n\n`JSON.parse_string()` returns every JSON number as a float. A test file:\n\n```\n{\"level\":3,\"big_int\":9007199254740993,\"float_one\":1.0,\"exp\":1e10,\"neg_zero\":-0.0,\"imprecise\":0.30000000000000004,\"tiny\":1.0000000000000002e-300,\"long_i64\":9223372036854775807}\n```\n\nAny round trip through a float parser turns `1.0` into `1`, `1e10` into `10000000000`, `-0.0` into `0`, and `9007199254740993` (2^53+1) into a neighbour. `9223372036854775807` does not survive at all. A game that compares `-0.0`, prints a value back, or hashes its own save will notice the difference.\n\nThe safe rule: read numbers as **source literals** (text), not as floats, and write back the literals you did not touch.\n\n`ConfigFile.save()` writes sections and `;` comments, but if the file is `project.godot` with an input map it also contains whole `Object(InputEventKey,...)` values. Measured: a 1040-byte `project.godot` with two input events. An INI library re-serialises the file without those `Object(...)` lines, and the project no longer opens in the editor.\n\nA 73-byte ConfigFile with a UTF-8 BOM, CRLF line endings and a `;` comment: `od -c` starts with `357 273 277` and every line ends `\\r\\n`. Parsers that normalise newlines or strips the BOM change lines you never selected. Keep them or the diff is a lie.\n\n`var_to_bytes()` files and `.res` are not text. An agent should refuse them, not \"repair\" them.\n\nThe tool I built opens the file with the engine's own parser to get labels and types, records the **byte span of the literal being changed**, and replaces exactly that span. Everything else is copied through untouched. Measured on the packaged build:\n\n`9007199254740993` → `1`, `\"Player One\"` → `\"ニュー 名前\"`, rest of the file byte-identical.` project.godot` with an input map: 1040 → 1038 bytes, both `Object(InputEventKey,...)` lines byte-identical afterwards, editor opened the project with no error.`--restore --write`: original SHA-256 back, exactly.\nExit codes that separate the failure modes, so the agent can branch instead of guessing from prose:\n\n`0` wrote (or dry run OK), `2` missing or unreadable (binary) file, `3` label missing, `null` leaf, or a type change, `4` file is read-only.`--write` then produces exactly those bytes. I checked that equality on Godot 4.4.1, 4.5.2, 4.6.3 and 4.7.2 with files that each engine wrote itself.`.sve.bak`) only on a real write, and no second backup when re-running the same edit (`no_change`).`--json` output. Note: the engine banner is the first line of stdout, so read the `--quiet` is not a workaround because it takes the JSON with it.\n\n```\ngodot --headless --path <project> --script res://addons/save_value_editor/edit.gd -- \\\n  --file=user://settings.cfg --set=audio/master=0.42 --set=progress/stage=7 --write\n```\n\n`--set` writes the literal as given, so a string needs its own quotes. That is deliberate (no number reformatting) and it is the one place where the agent has to know the format.\nThe same document code runs as a dock inside the editor, Godot 4.4+ — read a value list, filter it, change a value, see the diff before saving: [Save Value Editor for Godot](https://onpont.itch.io/save-value-editor). The command/exit-code contract and its limits are public here: [onpont/godot-save-value-cli](https://github.com/onpont/godot-save-value-cli).", "url": "https://wpnews.pro/news/editing-a-godot-save-file-from-outside-the-editor-what-actually-breaks", "canonical_source": "https://dev.to/onpont/editing-a-godot-save-file-from-outside-the-editor-what-actually-breaks-4ebk", "published_at": "2026-10-26 01:08:27+00:00", "updated_at": "2026-10-26 01:12:53.484978+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["Godot", "Save Value Editor", "Codex", "Claude Code", "onpont"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/editing-a-godot-save-file-from-outside-the-editor-what-actually-breaks", "markdown": "https://wpnews.pro/news/editing-a-godot-save-file-from-outside-the-editor-what-actually-breaks.md", "text": "https://wpnews.pro/news/editing-a-godot-save-file-from-outside-the-editor-what-actually-breaks.txt", "jsonld": "https://wpnews.pro/news/editing-a-godot-save-file-from-outside-the-editor-what-actually-breaks.jsonld"}}