Skip to content

fix(frontend): avoid leaking "{{resource}}" placeholder in NOT_FOUND errors#1762

Merged
looplj merged 1 commit into
looplj:unstablefrom
qinkangdeid:fix/i18n-notfound-resource-fallback
Jun 2, 2026
Merged

fix(frontend): avoid leaking "{{resource}}" placeholder in NOT_FOUND errors#1762
looplj merged 1 commit into
looplj:unstablefrom
qinkangdeid:fix/i18n-notfound-resource-fallback

Conversation

@qinkangdeid
Copy link
Copy Markdown
Contributor

Problem

Generic NOT_FOUND GraphQL errors carry no resource extension — e.g. opening a Request detail whose record belongs to a different project than the active x-project-id, where the relay node resolver returns:

{ "errors": [{ "message": "Could not resolve to a node with the global id of '8141'", "extensions": { "code": "NOT_FOUND" } }] }

useErrorHandler maps NOT_FOUNDcommon.errors.notFound, whose string interpolates {{resource}}. Since getErrorI18nParams only sets params.resource when extensions.resource is present, the placeholder gets no value and i18next leaves it verbatim, so the toast literally reads:

  • {{resource}} not found
  • {{resource}} 未找到

Fix

Add a localized common.errors.resourceFallback (Resource / 资源) and use it in useErrorHandler when a not-found error provides no resource name, so the message degrades to a readable Resource not found / 资源 未找到 instead of leaking the raw placeholder.

Targeted to the common.errors.notFound path only; errors that do carry extensions.resource are unchanged.

Changes

  • frontend/src/hooks/use-error-handler.ts — fallback when resource is absent
  • frontend/src/locales/{en,zh-CN}/base.json — new common.errors.resourceFallback key (en/zh in sync)

Testing

  • en / zh-CN base.json validated as JSON; key sets remain in parity.
  • Reproduced against a live v1.0.0-beta1 instance: opening a cross-project Request detail produced the {{resource}} toast; with this change it renders the fallback noun.

Generic NOT_FOUND GraphQL errors (e.g. relay node lookups such as opening
a Request that belongs to another project) carry no `resource` extension.
The `common.errors.notFound` string interpolates `{{resource}}`, so with
no value i18next leaves the raw placeholder in place and the toast shows
"{{resource}} not found" / "{{resource}} 未找到" to the user.

Add a localized `common.errors.resourceFallback` ("Resource" / "资源") and
use it in useErrorHandler when the not-found error provides no resource
name, so the message stays readable.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 2, 2026

Greptile Summary

This PR fixes a UI bug where NOT_FOUND GraphQL errors without an extensions.resource field caused the raw {{resource}} placeholder to be displayed verbatim in toast notifications. The fix adds a translated fallback noun ("Resource" / "资源") used only when the error carries no resource name.

  • use-error-handler.ts: After resolving the i18n key and params, a new guard checks for the common.errors.notFound path with no params.resource and populates the fallback via t('common.errors.resourceFallback'), leaving all other error paths and resource-bearing NOT_FOUND errors untouched.
  • en/base.json & zh-CN/base.json: New common.errors.resourceFallback key added in both locales, keeping the two locale files in parity.

Confidence Score: 5/5

Safe to merge — the change is minimal, well-scoped, and fixes a visible UI defect without touching any data or auth paths.

The logic is tightly targeted: the fallback is applied only when the resolved i18n key is exactly common.errors.notFound and params.resource is absent (falsy). All other error codes and all NOT_FOUND errors that already carry a resource name are completely unaffected. Both locale files are updated in sync, and the fallback itself is translated through the same i18n pipeline rather than being a hardcoded string.

No files require special attention.

Important Files Changed

Filename Overview
frontend/src/hooks/use-error-handler.ts Adds a fallback resource noun when a NOT_FOUND error carries no extensions.resource, preventing the raw {{resource}} placeholder from reaching the UI
frontend/src/locales/en/base.json Adds common.errors.resourceFallback: "Resource" key in parity with the zh-CN locale
frontend/src/locales/zh-CN/base.json Adds common.errors.resourceFallback: "资源" key in parity with the en locale

Sequence Diagram

sequenceDiagram
    participant UI as UI Component
    participant H as useErrorHandler
    participant P as error-parser
    participant i18n as i18next

    UI->>H: handleError(graphqlError)
    H->>P: parseGraphQLErrors(error)
    P-->>H: "[{ code: "NOT_FOUND", extensions: {} }]"
    H->>P: getErrorI18nKey("NOT_FOUND")
    P-->>H: "common.errors.notFound"
    H->>P: getErrorI18nParams(firstError)
    P-->>H: "{} (no resource extension)"
    Note over H: NEW: i18nKey === 'common.errors.notFound'<br/>AND !params.resource → set fallback
    H->>i18n: t('common.errors.resourceFallback')
    i18n-->>H: "Resource" / "资源"
    H->>i18n: "t('common.errors.notFound', { resource: "Resource" })"
    i18n-->>H: "Resource not found" / "资源 未找到"
    H-->>UI: toast.error("Resource not found")
Loading

Reviews (1): Last reviewed commit: "fix(frontend): fall back to generic noun..." | Re-trigger Greptile

@looplj looplj merged commit 974b28f into looplj:unstable Jun 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants