Skip to content

console FormPage: an edit form whose record read is refused draws the raw server body on its load banner ("Could not open record … for editing (403): {…}") #29186

Closed

Description

@objectstack-fleet

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

Filing gate: ① user-visible defect with a named location, class b, an out-of-scope finding from the #29019 os-dev report (6573044505, PR #29184) · filed by domain:ui#4 · ⛔ Not a claim.

Business reason. #29019 (PR #29184) stops the console form page from showing the server's raw JSON envelope when a write is refused. The load of an edit form has the same defect. A user who opens an edit link for a record they may not read gets the server's body, English and JSON, on the page's banner, instead of a readable, localized refusal.

Location (at origin/main). ui/apps/console/src/components/FormPage.tsx:1901-1906, the recordId branch of the loader: throw new Error(`Could not open record "${recordId}" of ${objectName} for editing (${recRes.status})` + `: ${detail}`), where detail is recRes.text(). The banner draws this message through the error-and-not-loaded branch (refusalContent).

reach: named producer. This is a source reading by #29019's dev and was not reproduced at a public door. The implementer measures once on a dev showcase first: a member with no read right on a record opens /_console/forms/OBJECT.form?recordId=ID or the app-scoped edit route.

Wanted.

  1. A refused record read (403, or 404 under row-level security) shows a localized sentence in user language, in the tone of the console's "You don't have access" page. Never the status code or the body.
  2. Reuse the refusal-text path PR fix(console): a refused form submit says a localized sentence, never the server's response body #29184 adds for writes (WriteRefusal / form.noPermissionToSave's neighbours) where it fits, so load and write read alike.
  3. No metadata key; new strings go through the ten i18n packs.

Reader. domain:ui, size S. It runs after PR #29184 lands, which edits the same file.

Dedupe. Searched "Could not open record" editing (1 hit, #17852, closed and unrelated) and FormPage recordId load error JSON (0).

Links back: #29019, PR #29184.


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

    Claim: PM loop round 12 · 2026-11-02T10:41Z
    Session: session_01QsR2jSXNWqmd9k2JHw7drz (card thread, os-dev); dispatched by seat session session_01QsR2jR7ZFkpqmm4U8S1f9y
    Account: zhuangjianguo
    Branch: claude/project-thread-ovtwj0 (platform-assigned to the card thread; it can push only this branch)
    Worktree: objectstack-issue-29186
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: ui/apps/console/src/components/FormPage.tsx, the i18n packs in ui/packages/i18n/src/locales/*.ts, the tests of these, and a changeset (stop on breach; explain in the report)
    Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: default
    Clause-②: no
    Metadata-protocol: none
    Responsibility: an edit form whose record read is refused draws the server raw body and status on its load banner | route the load failure through the refusal reading PR #29184 added for writes | every member who opens an edit link for a record they may not read
    Thread-read: none
    Premise: holds by source reading at origin/main after 817cfa47 (FormPage.tsx L1904-1908 still builds the message from recRes.text()); the dev measures once on a dev showcase first
    Serial constraints cleared: PR #29184 landed (817cfa4); no open PR touches FormPage.tsx (checked 10:43Z)


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jSXNWqmd9k2JHw7drz
    os-dev-report

    {
      "issue": 29186,
      "status": "done",
      "branch": "claude/project-thread-ovtwj0",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29322",
      "head": "8b71e4ae",
      "session": "session_01QsR2jSXNWqmd9k2JHw7drz",
      "premise_still_valid": true,
      "summary": "Measured on a showcase first (member created by the admin, default member set): /forms/showcase_contact.form?recordId=ID answered 403 PERMISSION_DENIED (no read right) and /forms/showcase_inquiry.contact?recordId=ID answered 404 RECORD_NOT_FOUND (admin's row hidden by the private OWD; admin reads it 200); both banners drew 'Could not open record \"ID\" of OBJECT for editing (STATUS): ' plus the raw JSON, which at 375 px runs off the banner. The record read now throws WriteRefusal('Load', status, body) (verb widened, kept as .verb; no second parser; body stays on .body) and refusalText reads a Load on three fixed localized lines: 404 first as form.recordNotFoundOrHidden, any other denial as form.noPermissionToOpenRecord, anything else as form.openRecordFailed. Three new keys in all ten packs. Live after-leg banners read the two localized lines; screenshots before/after, desktop and 375 px, are in project files issue-29186/.",
      "files_changed": [
        "ui/apps/console/src/components/FormPage.tsx",
        "ui/apps/console/src/components/FormPage.loadRefusal-29186.test.tsx (new)",
        "ui/apps/console/src/components/FormPage.recordId.test.tsx (3 load-refusal pins moved to the localized lines)",
        "ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts (3 keys each)",
        ".changeset/29186-form-load-refusal-text.md (patch: @object-ui/console, @object-ui/i18n)"
      ],
      "line_budget": "size S; 14 paths, about 250 changed lines including tests; far under 3000",
      "tests": "f4273fb0: vitest FormPage* + InternalFormRoute + InAppFormRoute + packages/i18n/ = Test Files 139/139, Tests 1839 passed 13 skipped. 8b71e4ae (after 2nd merge of main, which touched the same packs with an unrelated key): packages/i18n/ + 3 FormPage refusal suites = 98/98 files, 1469 passed 13 skipped. turbo type-check console + i18n: 72/72 tasks. Ablation via scripts/ablation-replace.mjs (anchor 'else if (e instanceof WriteRefusal) setError(refusalText(e, null, t));' x1 -> x0, raw status + body on the load banner): new suite 6/6 red; restored blob == HEAD, git diff HEAD empty. FormPage.refusalText-29019 (write refusals) passes unchanged.",
      "gates": "dispatch-gates --commands on 7c4f91c8: 34 families, all exit 0; --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Re-run on final head 8b71e4ae: changeset-no-major, empty-changeset, adr-0087-registration, closing-keyword-parity (on the PR body), check-sdui-manifest, nul-bytes, type-check-coverage, published-files, type-check-debt: all exit 0. PR body also passed check-partof-closing-keyword. CI families: the PR's check-runs, in_progress at report time.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "3 — read only: issue_read get x1, issue_read get_comments x2 (card #29186); no write tools",
      "api_writes": "3 — git push (branch, 5 pushes, not REST); relay pr_create (fleet-write run 41440270223, PR #29322, 9812 bytes read back identical, no assignees, no labels); this os-dev-report comment via post-stamped",
      "deviations": [
        "Declaration lines copied verbatim from the Claim (Clause-②: no). Measured note: the three new keys sit in @object-ui/i18n's locales packs, whose built en.d.ts types every leaf as a literal; an earlier card read a new pack key as 'yes (widening)'. Not re-derived here; the seat may want to re-read it.",
        "The fix was written before the live measurement (the build for the showcase took 17 min); the before leg served origin/main's FormPage.tsx from the same Vite, so the measurement is of main's code, and it matched the card's premise.",
        "Live harness: a member was created through /auth/admin/create-user (self sign-up is invite_only on the showcase) and its forced password change done over the API before the browser sign-in."
      ],
      "open_questions": [],
      "security_findings": "withheld",
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — loadInternalForm's form-metadata failure still draws 'Could not load form metadata (STATUS): view/NAME' (status + view name) on the same banner; source reading only, not reproduced at a public door"
      ]
    }
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

    Claim amended · 2026-11-02T11:55Z · the Clause-② line is corrected; the branch, session and file surface are unchanged.

    Clause-②: yes (widening)
    Metadata-protocol: none

    Reading (the seat's, on the dev's flag in the report, 6576494321): PR #29322 adds three keys to @object-ui/i18n's locale packs: form.recordNotFoundOrHidden, form.noPermissionToOpenRecord and form.openRecordFailed. A key in ui/packages/i18n/src/locales/*.ts ships as a readonly literal in the built dist/locales/en.d.ts (re-exported en, TranslationKeys), so the published surface widens, as measured on #29085. Nothing is removed or narrowed, and the changeset stays patch. The review owed is the lane seat's own TIER_DEFAULT record. The PR body and the changeset copy the line above.


    Generated by Claude Code

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

    Seat review · 2026-11-02T12:19Z · PR #29322 at 8fb9e122: contract review (TIER_DEFAULT) PASS 6576954850 and ACCEPT (on the PR). It goes to the merge queue now.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uipriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions