Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

AppManagementPage's search filter calls .toLowerCase() on app.label, which throws when the label is the keyed I18nLabel form #4343

Closed

Description

@yinlianghui

Found while implementing #4307 (console i18n conversion for this page). Filed unassigned, NOT fixed there — out of that card's scope, which is the page's own chrome.

Fact

apps/console/src/pages/system/AppManagementPage.tsx, the search filter:

const filteredApps = (apps || []).filter((app) => {
  if (!searchQuery) return true;
  const q = searchQuery.toLowerCase();
  return (
    (app.name || '').toLowerCase().includes(q) ||
    (app.label || '').toLowerCase().includes(q) ||       // ← here
    (app.description || '').toLowerCase().includes(q)
  );
});

app.label may be objectui's KEYED i18n label form — an object { key, defaultValue?, params? }. The same file resolves exactly that shape through resolveKeyedI18nLabel for display, which is why the resolver is imported there at all. An object is truthy, so (app.label || '') yields the object and .toLowerCase is undefined:

TypeError: (l || "").toLowerCase is not a function

app.description is the same shape and the same hazard on the line below it.

Impact

The throw happens inside filter during render, so it takes out the page rather than degrading search — but only once the operator types something (if (!searchQuery) return true short-circuits the empty case). So the page loads fine and breaks on first keystroke.

Not measured, and it decides the severity: whether any shipping app metadata actually carries a keyed label/description today. The render path is defensive about it and the search path is not, which is the inconsistency worth recording either way; the grading of "live crash" versus "latent" is the triage round's, not this filing's.

Suggested shape

Route both reads through the resolver the file already imports, so search matches what the user actually sees rather than a second, less capable reading of the same field:

(resolveKeyedI18nLabel(app.label) || '').toLowerCase().includes(q)

Worth checking whether sibling list/search surfaces filter on a raw label the same way before fixing just this one.

Related: #4163 (the I18nLabel audit — that one is the spec's INLINE per-locale map form; this is objectui's keyed form, a different shape with the same class of unguarded read).


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 11, 2026
  2. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    ContributorAuthor

    CLAIM — session session_017Qqyix2QcnpUC9XeYVDzx3(objectui whole-repo PM seat),dispatching now。Branch: claude/issue-4343-search-filter-keyed-label。闸门:PR #4344 落地(同页面文件)。

    范围:AppManagementPage 搜索过滤的 (app.label || '').toLowerCase() 对 keyed I18nLabel 抛 TypeError(对象恒真,|| '' 拦不住)——复用 #4344 刚在同文件立起的 resolveKeyedI18nLabel(label, t) 解析后再 toLowerCase,不发明第二条解析路径。附测量令:实测发运的 app 元数据(showcase/examples + spec 的 AppSchema label 类型)今天是否真能携带 keyed label——这决定严重级,写进 PR 正文;可携带则红先用例复现首键崩溃,不可携带则按防御性修复记 patch。同文件扫描同型读(.toLowerCase()/字符串方法直接作用于 I18nLabel 槽)一并收,异型报 finding。


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions