fix(studio): a stale project id in the URL no longer blocks editing for the tab - #4188
Open
vanceingalls wants to merge 1 commit into
Open
vanceingalls wants to merge 1 commit into
vanceingalls wants to merge 1 commit into
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
…or the tab The project id in the location hash is user-supplied and outlives the project it names: rename the folder, or open a bookmark from a project that is gone, and it points at nothing. useServerConnection trusted it unconditionally, so every later /api/projects/<id>/... request 404'd for the life of the tab — including the composition read that opens the SDK session. Studio then fell back to the server path for every edit, and the resolver shadow never ran either, so nothing recorded why. Telemetry, 24h after the read-reason split shipped in v0.8.51: 120 `stage: read` / `http_error` 404 reads across 5 users, about 24 each, none recovering. On this route a 404 has exactly one origin — resolveProject returned null — since the other failure modes answer 403 and the GET does not set mustExist. Validates through /api/projects/:id, which calls the same adapter.resolveProject the file routes use. Matching against the /api/projects list instead would be wrong twice over: that list omits session ids, which resolve fine, and skips project dirs without an index.html. The check is tri-state on purpose. Only a definite 404 counts as missing; a rejected request or a 5xx keeps the hash, so one network blip cannot rewrite a valid deep link out from under the user. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vanceingalls
force-pushed
the
studio-hash-project-validate
branch
from
September 19, 2026 20:42
6b1362b to
50a360c
Compare
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.
The bug
The project id in the location hash is user-supplied and outlives the project it names. Rename the folder, or open a bookmark from a project that's gone, and it points at nothing.
useServerConnectiontrusted it unconditionally:So every later
/api/projects/<id>/...request 404s for the life of the tab — including the composition read that opens the SDK session. Studio falls back to the server path for every edit, and the resolver shadow never runs either, so nothing records why.How it was found
The
stage: readreason split shipped in v0.8.51. Within 24 hours it named the cause:http_errorabsent_or_empty~24 events per user, none recovering — the shape of a bad hash that never gets rewritten. A missing file answers
200 + ""and shows asabsent_or_empty, so these are genuinely unresolved projects, not missing compositions.On this route a 404 has exactly one origin —
adapter.resolveProjectreturned null. The other failure modes answer 403, and the GET doesn't setmustExist.The fix
Validate through
GET /api/projects/:id, which calls the sameadapter.resolveProjectthe file routes use. If it's gone, fall back to the first project and rewrite the hash — the path a hashless load already takes.Why not match against the
/api/projectslist (which the same code already fetches): that would be wrong twice over.resolveProjectresolves fine viasessions/<id>.json.index.html/<name>.html.Either would turn a working deep link into a silent redirect.
The check is tri-state on purpose. Only a definite 404 counts as
missing; a rejected request or a 5xx returnsunknownand keeps the hash. A network blip must not rewrite a valid deep link out from under the user.Before
Loading
#project/deleted-project— a hash id the server cannot resolve (GET /api/projects/deleted-project→ 404). The hash is left untouched, the file tree never loads, and the composition read 404s for the life of the tab: "No compositions found",00:00/00:00.After
Same URL, same server. The unresolvable id is detected, Studio falls back to the real project and rewrites the hash to
#project/demo-capture— tree loaded,index.htmlopen,00:00/00:03.Verification
unknownintomissingfails both keep-the-hash tests.packages/studio: 99 files, 843 tests pass.tsc --noEmitclean, oxlint 0/0, oxfmt clean.Note on the remaining
absent_or_emptyThe other 10 events are not covered here and I'm not guessing at them. The obvious cause — a stale comp path in the same URL — is already guarded:
useHydrateActiveCompPathFromUrlrunsnormalizeStudioCompositionPath(urlPath, fileTree)against the loaded tree. So that class needs its own root-cause rather than a speculative fix.🤖 Generated with Claude Code