fix(console): read the SETTINGS_LOCKED key from error.details, tolerating both shapes (objectstack#4224) - #3079
Merged
Conversation
…rating both shapes (objectstack#4224) `SettingsView.onSave` rendered its lock toast from `err.payload.error.key`. That key was a SIBLING of `code`/`message` inside `error`, a position `ApiErrorSchema` never declared — it reached the console only because the schema is a plain `z.object` and strips undeclared keys instead of rejecting them, so nothing on either side of the wire ever flagged it. objectstack#4224 moves it to `error.details.key`, the slot the contract does declare. This is the console's half and ships FIRST: the read is now the declared slot with the old position as fallback, so the toast keeps naming the locked key against servers on either side of that change rather than degrading to "Locked by environment: undefined" during the window where the two repos are on different versions. The fallback goes once the oldest supported server carries the fix. It lives in `settings/api.ts` as `lockedKeyOf` rather than inline in the view, because that module already owns what the server's error body looks like (`jsonOrThrow` is what builds `err.payload`) and because a compat shim no test exercises is one the next reader deletes half of. Both branches are pinned, so removing either is a red test naming the server version it breaks. Also stops interpolating a missing key: with neither position carrying one the toast reads "Locked by environment" rather than appending `undefined`. This was the only in-console reader of the four keys objectstack#4224 relocated — a repo-wide grep for `namespace` / `reason` / `fields` in that position finds no consumer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tv2NYMBTrzVVRWiWWuyec5
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-zhuang
marked this pull request as ready for review
July 31, 2026 00:39
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 console half of objectstack-ai/objectstack#4224. This should land first — see Ordering.
What
SettingsView.onSaverendered its lock toast fromerr.payload.error.key:error.keywas a sibling ofcode/messageinsideerror— a positionApiErrorSchemanever declared. It reached the console only because that schema is a plainz.objectand strips undeclared keys instead of rejecting them, so nothing on either side of the wire ever flagged it. objectstack#4224 moves the key toerror.details.key, the slot the contract does declare.The change
A
lockedKeyOfhelper insettings/api.tsreads the declared slot first and falls back to the old position, so the toast keeps naming the locked key against servers on either side of that change rather than degrading toLocked by environment: undefinedduring the window where the two repos are on different versions. The fallback can go once the oldest supported server carries the fix.It lives in
api.tsrather than inline in the view for two reasons: that module already owns what the server's error body looks like (jsonOrThrowis what buildserr.payload), and a compat shim no test exercises is one the next reader deletes half of. Both branches are pinned inapi.test.ts, so removing either is a red test that names the server version it breaks — which is also the signal for when the old branch may legitimately go.Also stops interpolating a missing key: with neither position carrying one, the toast now reads
Locked by environmentrather than appendingundefined.Ordering
Tolerant-read-first, the objectui#2869 pattern ADR-0112 records:
error.detailsLanding them the other way round shows
Locked by environment: undefinedto anyone on a console built before the server ships.Scope
error.keywas the only in-console reader of the four keys objectstack#4224 relocates. A repo-wide grep fornamespace/reason/fieldsread from that position finds no consumer, so nothing else needs a dual-read.Worth noting for later:
extractFieldErrors(packages/react/src/utils/error-message.ts) already readsdetails.fields, and objectstack#4224 turnsSETTINGS_VALIDATION's field map into the declaredFieldError[]under exactly that path. Settings validation failures therefore become renderable per-field with no further change here — wiring the settings form to actually do that is a separate piece of work, not part of this compat fix.Verification
pnpm --filter @object-ui/console test— 15 files, 111 tests, green (4 of them new)tsc --noEmitreports nothing in the touched files; the remaining errors in this app are pre-existing unresolved@object-ui/*workspace imports (sibling packages not built in this environment), present on the base branch too🤖 Generated with Claude Code
Generated by Claude Code