fix(launcher): never replace a profile state file we could not read - #77
Merged
Conversation
A profile that had worked for months came up unusable: a blank side panel, a
New Tab that never finished loading. A fresh profile on the same machine, same
build, same engine, with the same extension loaded, was fine — so the profile
was the fault, not the code it ran.
Three blocks here edit Chromium's JSON state. Every one of them did:
try:
d = json.load(open(p)) if os.path.exists(p) else {}
except Exception:
d = {}
... json.dump(d, open(p, "w"))
Both halves are wrong, and they feed each other. The write is in-place and
truncating, so an interrupted launch leaves a half-written file. The read then
treats that file as absent and writes a stub holding only the key that block
cared about. Default/Preferences IS the profile — search engine, startup, every
extension's state — so the second launch after an interrupted one silently
factory-resets it, and nothing says so.
So: a state file that exists and does not parse is now left exactly as found,
with a line on stderr naming it. Refusing to write is not refusing to start —
the browser still launches, just without that block's setting applied. And all
three writes now land as a rename (fsync, then os.replace) instead of in place,
so there is no longer a truncated file for the next launch to misread. #75
fixed the write half for the search block alone and left its read, and left the
other two blocks untouched; this finishes the job.
Tests: 5 cases — a corrupt Preferences and a corrupt Local State survive a
launch byte-for-byte, the refusal is announced, the browser still starts, and
setting restore_on_startup keeps every unrelated key. Verified 3 of the 5 fail
against the pre-fix launcher.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ThreatCrush Security Scan93 finding(s) HIGH/CRITICAL: 6 | MEDIUM: 87
…and 43 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
`pnpm install --frozen-lockfile` failed on this branch with ENOENT trying to mkdir apps/desktop/node_modules — because the path was checked in as a symlink pointing at an absolute path that exists on no runner. It got committed because .gitignore said `node_modules/`, and a trailing slash matches directories only. A symlink named node_modules — which is what running the suite against a hoisted store leaves behind — is not a directory, so it was never ignored. Dropping the slash covers both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ralyodio
added a commit
that referenced
this pull request
Aug 4, 2026
main moved twice underneath this branch: #77 rewrote the profile-state blocks and #78 reworded the Manifest V2 comment after uBlock stopped being bundled. The conflict was that comment — this branch still carried the old wording, with the GPU block inserted above it. Kept main's wording and the GPU block, ordered so the block runs before MV2_KEEP is used. The branch was parked as WIP with no tests, and launcher flag handling is exactly what this suite covers, so it should not merge without them. Six cases: each mode's effect on the command line, that `safe` appends Vulkan to the disable-features list without displacing the MV2 kill switch it shares that list with, that an unknown mode falls back to `on` and says so, and that TRONBROWSER_GPU overrides the stored mode. Verified 5 of the 6 fail against main's launcher; the sixth asserts the default, which holds either way. Still worth saying plainly: this is an escape hatch, not a diagnosis. Launching with --disable-gpu did NOT fix the freeze it was written for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Evidence
A profile that had worked for months came up unusable — blank side panel, New Tab stuck loading. On the same machine, same build, same engine:
tron(existing profile)tron --user-data-dir=/tmp/tb-cleantron --user-data-dir=/tmp/tb-clean2 --disable-extensionsMoshpit mode read
clearnet(the default — no registry lookups on real endings). So: not the extension, not moshpit, not the engine. The profile was the fault, not the code it ran.The defect
Three blocks in the launcher edit Chromium's JSON state files. Every one of them did:
Both halves are wrong, and they feed each other:
Default/Preferencesis the profile — search engine, startup behaviour, every extension's state. So the second launch after an interrupted one silently factory-resets it, and nothing tells you.Local Statehas the same shape.The fix
fsyncthenos.replace) rather than in place, so there is no longer a truncated file for the next launch to misread.#75 fixed the write half for the search block alone, left its read, and left the other two blocks untouched. This finishes it.
Tests
5 cases: corrupt
Preferencesand corruptLocal Stateeach survive a launch byte-for-byte; the refusal is announced; the browser still starts; and settingrestore_on_startuppreserves every unrelated key.Verified 3 of the 5 fail against the pre-fix launcher — the two clobber cases and the announcement.
Full desktop suite: 169 passed.
Not covered
This stops profiles from being damaged. It does not repair one already damaged — that needs the bad file moved aside so Chromium regenerates it.
🤖 Generated with Claude Code