fix(frontend): avoid leaking "{{resource}}" placeholder in NOT_FOUND errors#1762
Conversation
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 SummaryThis PR fixes a UI bug where
Confidence Score: 5/5Safe 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 No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
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")
Reviews (1): Last reviewed commit: "fix(frontend): fall back to generic noun..." | Re-trigger Greptile |
Problem
Generic
NOT_FOUNDGraphQL errors carry noresourceextension — e.g. opening aRequestdetail whose record belongs to a different project than the activex-project-id, where the relaynoderesolver returns:{ "errors": [{ "message": "Could not resolve to a node with the global id of '8141'", "extensions": { "code": "NOT_FOUND" } }] }useErrorHandlermapsNOT_FOUND→common.errors.notFound, whose string interpolates{{resource}}. SincegetErrorI18nParamsonly setsparams.resourcewhenextensions.resourceis 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 inuseErrorHandlerwhen a not-found error provides no resource name, so the message degrades to a readableResource not found/资源 未找到instead of leaking the raw placeholder.Targeted to the
common.errors.notFoundpath only; errors that do carryextensions.resourceare unchanged.Changes
frontend/src/hooks/use-error-handler.ts— fallback whenresourceis absentfrontend/src/locales/{en,zh-CN}/base.json— newcommon.errors.resourceFallbackkey (en/zh in sync)Testing
en/zh-CNbase.json validated as JSON; key sets remain in parity.{{resource}}toast; with this change it renders the fallback noun.