refactor(web): prevent query failures from forcing 500 redirects - #6762
refactor(web): prevent query failures from forcing 500 redirects#6762seefs001 wants to merge 2 commits into
Conversation
WalkthroughThe web application now validates failed API responses, propagates errors through React Query and custom hooks, and renders retryable error states across tables, dashboards, pages, and data-entry drawers. Shared retry logic now replaces the global query-cache error handler. ChangesUnified error handling
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/features/dashboard/components/overview/overview-dashboard.tsx (1)
481-496: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winRender an error state for initial query failures.
Both components swallow rejected
useQueryfailures and show fallback UI as if data is absent.
web/src/features/dashboard/components/overview/overview-dashboard.tsx#L480: handleapiKeysQuery/modelsQuery.isErrorwith retry controls; avoid leaving the model signal atLoadingor using request-example defaults.web/src/features/pricing/components/model-details.tsx#L185: handlemetricsQuery.isErrorbefore deriving TPS/latency/success-rate metrics; avoid showing zero or unavailable values.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/features/dashboard/components/overview/overview-dashboard.tsx` around lines 481 - 496, Handle initial query errors in web/src/features/dashboard/components/overview/overview-dashboard.tsx:481-496 by checking apiKeysQuery.isError and modelsQuery.isError before deriving fallback UI, rendering an error state with retry controls, and preventing the model signal from remaining Loading or using request-example defaults. In web/src/features/pricing/components/model-details.tsx:185, check metricsQuery.isError before deriving TPS, latency, or success-rate metrics and render the corresponding error state with retry controls instead of zero or unavailable values.
🧹 Nitpick comments (2)
web/src/features/users/components/users-mutate-drawer.tsx (1)
293-297: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse
cn()for the conditional form class.Replace the template literal with
cn(sideDrawerFormClassName(), (drawerLoading || drawerError) && 'hidden').As per coding guidelines: “动态类名使用
cn()合并。”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/features/users/components/users-mutate-drawer.tsx` around lines 293 - 297, Update the form className in the Form submission block to use cn() for combining sideDrawerFormClassName() with the conditional 'hidden' class when drawerLoading or drawerError is true, replacing the current template literal.Source: Coding guidelines
web/src/features/about/api.ts (1)
25-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd explicit return types to the changed exported functions and hook.
These exports rely on inferred return types. Declare their return types to make their public contracts stable.
web/src/features/about/api.ts#L25-L30: declare thePromise<AboutResponse>return type.web/src/features/system-settings/api.ts#L36-L41: declare the successful system-options response return type.web/src/features/system-settings/auth/custom-oauth/hooks/use-custom-oauth-providers.ts#L24-L34: declare the React Query result type.Run the repository Bun typecheck and lint scripts after adding the types.
As per coding guidelines, “参数和返回值应显式标注类型.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/features/about/api.ts` around lines 25 - 30, Declare explicit return types for getAboutContent in web/src/features/about/api.ts lines 25-30 as Promise<AboutResponse>; for the changed system-options API function in web/src/features/system-settings/api.ts lines 36-41, use its successful response type; and for the custom OAuth provider hook in web/src/features/system-settings/auth/custom-oauth/hooks/use-custom-oauth-providers.ts lines 24-34, declare the appropriate React Query result type. Run the repository Bun typecheck and lint scripts afterward.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/features/channels/components/drawers/channel-mutate-drawer.tsx`:
- Line 865: Update channel-mutate-drawer.tsx:865 so isChannelDetailError is true
only when channelQuery.isError and channelQuery.data?.data is missing. Apply the
same cached-data guard to isModelDetailError in model-mutate-drawer.tsx:288-289.
In users-mutate-drawer.tsx:179-188, set drawerLoading and drawerError only when
the groups, permission catalog, or user-details query fails without usable
cached data; preserve editing and saving when cached data remains available.
In `@web/src/features/performance-metrics/api.ts`:
- Around line 30-45: Strengthen requirePerformanceMetricsResponse by validating
every required field of each groups item, including group, avg_ttft_ms, and
series, before casting to SuccessfulPerformanceMetricsData. Reject malformed
entries such as empty objects while preserving the existing failure error
behavior and valid-response path.
In `@web/src/features/profile/hooks/use-profile.ts`:
- Around line 54-58: The silent error path in fetchProfile currently only logs
failures, leaving refreshProfile and callers such as
updateProfile/updateSettings with stale profile data and no user-visible
recovery. Update fetchProfile and refreshProfile so silent-refresh failures are
stored and exposed through the existing profile error state, with an appropriate
retry or stale-data recovery path; otherwise make the silent-refresh contract
explicit and ensure callers do not report success when refresh fails.
In `@web/src/features/rankings/api.ts`:
- Around line 42-45: Validate all required fields before narrowing API
responses: in web/src/features/rankings/api.ts lines 42-45, update the guard
around the rankings response so RankingsSnapshot requires models, vendors,
top_movers, top_droppers, models_history, and vendor_share_history; in
web/src/features/pricing/api.ts lines 32-39, extend the existing guard to
require group_ratio, usable_group, supported_endpoint, and auto_groups in
addition to data and vendors, while returning the unchanged response when valid.
In `@web/src/features/system-settings/components/settings-page.tsx`:
- Around line 142-154: Only render the settings-page ErrorState when
optionsQuery.isError and data is undefined, preserving cached data during failed
refetches; update the corresponding condition in
web/src/features/system-settings/components/settings-page.tsx lines 142-154.
Apply the same guard to the custom OAuth ErrorState in
web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsx
lines 78-88, requiring providersQuery.isError and providersQuery.data ===
undefined.
In `@web/src/features/users/api.ts`:
- Line 178: Update the API error handling around the fallback in the relevant
users API validator to replace the hardcoded “Request failed” text with the
existing i18next translation function t('Request failed'), preserving the
server-provided res.data?.message when available.
In `@web/src/features/users/components/users-mutate-drawer.tsx`:
- Around line 117-129: Update the groups query in the users mutate drawer to use
a unique, consistently scoped array query key that identifies this user-scoped
string-list query rather than the shared ['groups'] key. Keep the existing
getGroups response validation and groupsQuery.data fallback unchanged, and
ensure the key does not collide with other groups queries.
In `@web/src/features/wallet/hooks/use-topup-info.ts`:
- Line 179: Update the error handling in useTopupInfo so the fallback for
response.message uses the project i18n instance instead of the hard-coded
“Request failed” text, and add the translation key to locale resources if
absent. Preserve response.message when provided.
---
Outside diff comments:
In `@web/src/features/dashboard/components/overview/overview-dashboard.tsx`:
- Around line 481-496: Handle initial query errors in
web/src/features/dashboard/components/overview/overview-dashboard.tsx:481-496 by
checking apiKeysQuery.isError and modelsQuery.isError before deriving fallback
UI, rendering an error state with retry controls, and preventing the model
signal from remaining Loading or using request-example defaults. In
web/src/features/pricing/components/model-details.tsx:185, check
metricsQuery.isError before deriving TPS, latency, or success-rate metrics and
render the corresponding error state with retry controls instead of zero or
unavailable values.
---
Nitpick comments:
In `@web/src/features/about/api.ts`:
- Around line 25-30: Declare explicit return types for getAboutContent in
web/src/features/about/api.ts lines 25-30 as Promise<AboutResponse>; for the
changed system-options API function in web/src/features/system-settings/api.ts
lines 36-41, use its successful response type; and for the custom OAuth provider
hook in
web/src/features/system-settings/auth/custom-oauth/hooks/use-custom-oauth-providers.ts
lines 24-34, declare the appropriate React Query result type. Run the repository
Bun typecheck and lint scripts afterward.
In `@web/src/features/users/components/users-mutate-drawer.tsx`:
- Around line 293-297: Update the form className in the Form submission block to
use cn() for combining sideDrawerFormClassName() with the conditional 'hidden'
class when drawerLoading or drawerError is true, replacing the current template
literal.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 34cec0d1-a20d-4064-b363-865267e631d3
📒 Files selected for processing (43)
web/src/components/data-table/layout/data-table-page.tsxweb/src/components/error-state.tsxweb/src/features/about/api.tsweb/src/features/about/index.tsxweb/src/features/channels/components/channels-table.tsxweb/src/features/channels/components/drawers/channel-mutate-drawer.tsxweb/src/features/dashboard/components/models/performance-overview.tsxweb/src/features/dashboard/components/overview/overview-dashboard.tsxweb/src/features/dashboard/components/overview/performance-health-panel.tsxweb/src/features/dashboard/components/users/user-charts.tsxweb/src/features/errors/general-error-status.tsweb/src/features/errors/general-error.tsxweb/src/features/keys/components/api-keys-table.tsxweb/src/features/legal/legal-document.tsxweb/src/features/models/components/deployments-table.tsxweb/src/features/models/components/drawers/model-mutate-drawer.tsxweb/src/features/models/components/models-table.tsxweb/src/features/performance-metrics/api.tsweb/src/features/performance-metrics/types.tsweb/src/features/playground/api.tsweb/src/features/pricing/api.tsweb/src/features/pricing/components/model-details-performance.tsxweb/src/features/pricing/components/model-details.tsxweb/src/features/pricing/index.tsxweb/src/features/profile/hooks/use-profile.tsweb/src/features/profile/index.tsxweb/src/features/rankings/api.tsweb/src/features/redemption-codes/components/redemptions-table.tsxweb/src/features/subscriptions/components/subscriptions-table.tsxweb/src/features/system-settings/api.tsweb/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsxweb/src/features/system-settings/auth/custom-oauth/hooks/use-custom-oauth-providers.tsweb/src/features/system-settings/components/settings-page.tsxweb/src/features/usage-logs/components/common-logs-stats.tsxweb/src/features/usage-logs/components/usage-logs-table.tsxweb/src/features/users/api.tsweb/src/features/users/components/users-mutate-drawer.tsxweb/src/features/users/components/users-table.tsxweb/src/features/wallet/hooks/use-topup-info.tsweb/src/features/wallet/index.tsxweb/src/lib/api.tsweb/src/lib/query-retry.tsweb/src/main.tsx
| const isBatchMode = | ||
| multiKeyMode === 'batch' || multiKeyMode === 'multi_to_single' | ||
| const isChannelDetailLoading = isEditing && isChannelLoading | ||
| const isChannelDetailError = isEditing && channelQuery.isError |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
According to official TanStack Query v5 documentation, when a background refetch fails after cached query data exists, are dataretained andisError set to true?
💡 Result:
According to the official TanStack Query v5 documentation, when a background refetch fails and cached data already exists, the library retains the previous successful data while also updating the state to reflect the error [1]. Specifically, in this scenario: - Data retention: The previously fetched data remains available to the application [1]. - Error state: The query will enter an error state, meaning isError will be set to true and the error property will be populated with the failure information [2][3][1]. - Refetch-specific state: To help distinguish this from a "hard" error (where no data was ever successfully fetched), the library provides the isRefetchError boolean, which will be true when a background refetch fails [2][4]. This behavior is designed to allow developers to continue displaying the existing data to the user while simultaneously acknowledging the background update failure [1].
Citations:
- 1: Inconsistent refetching state for errored queries TanStack/query#5988
- 2: https://tanstack.com/query/v5/docs/framework/react/reference/useQuery
- 3: https://tanstack.com/query/v5/docs/framework/react/guides/queries
- 4: https://tanstack.com/query/latest/docs/framework/react/reference/useQuery
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for f in \
"web/src/features/channels/components/drawers/channel-mutate-drawer.tsx" \
"web/src/features/models/components/drawers/model-mutate-drawer.tsx" \
"web/src/features/users/components/users-mutate-drawer.tsx"
do
echo "===== $f ====="
wc -l "$f"
ast-grep outline "$f" --view concise | sed -n '1,220p'
echo
doneRepository: QuantumNous/new-api
Length of output: 467
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "===== relevant channel mutate drawer excerpts ====="
sed -n '830,885p' web/src/features/channels/components/drawers/channel-mutate-drawer.tsx
echo
sed -n '1,120p' web/src/features/channels/components/drawers/channel-mutate-drawer.tsx
echo "===== relevant model mutate drawer excerpts ====="
sed -n '260,310p' web/src/features/models/components/drawers/model-mutate-drawer.tsx
echo
sed -n '1,110p' web/src/features/models/components/drawers/model-mutate-drawer.tsx
echo "===== relevant users mutate drawer excerpts ====="
sed -n '155,205p' web/src/features/users/components/users-mutate-drawer.tsx
echo
sed -n '1,120p' web/src/features/users/components/users-mutate-drawer.tsxRepository: QuantumNous/new-api
Length of output: 15941
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for f in \
"web/src/features/channels/components/drawers/channel-mutate-drawer.tsx" \
"web/src/features/models/components/drawers/model-mutate-drawer.tsx" \
"web/src/features/users/components/users-mutate-drawer.tsx"
do
echo "===== usages in $f ====="
python3 - <<'PY' "$f"
import re, sys
p = sys.argv[1]
text = open(p, encoding='utf-8').read()
for name in ['isChannelDetailError','isModelDetailError','drawerError','drawerLoading']:
if name not in text:
continue
print(f'--- {name} ---')
for m in re.finditer(re.escape(f'{{{name} |') | re.escape(f'{{ {name}') | re.escape(f',{name}') | re.escape(f'{name}:'), text):
i = text.rfind('\n', 0, m.start())
print(f'{i+1-1}:', text[i+1:i+1000].split('\n', 1)[0])
print(f'{i+1+1}:', text[i+1:i+1000].split('\n', 1, maxsplit=1)[1].split('\n', 1)[0])
break
PY
echo
doneRepository: QuantumNous/new-api
Length of output: 414
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "===== channel drawer query refs and key definitions ====="
rg -n "channelQuery|isEditing|isChannelDetailError|isChannelDetailLoading|channel.*Query|queryKeys" web/src/features/channels/components/drawers/channel-mutate-drawer.tsx
echo
echo "===== model drawer query refs ====="
rg -n "modelQuery|isEditing|isModelDetailError|isModelDetailLoading|model.*Query|queryKeys" web/src/features/models/components/drawers/model-mutate-drawer.tsx
echo
echo "===== users drawer query refs ====="
rg -n "userDetailQuery|groupsQuery|permissionCatalogQuery|drawerError|drawerLoading|isUpdate" web/src/features/users/components/users-mutate-drawer.tsx
echo
echo "===== error/loading state components ====="
rg -n "ErrorState|LoadingState" web/src/features/channels/components/drawers/channel-mutate-drawer.tsx web/src/features/models/components/drawers/model-mutate-drawer.tsx web/src/features/users/components/users-mutate-drawer.tsxRepository: QuantumNous/new-api
Length of output: 8455
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "===== channel drawer surrounding fetch/conditionals ====="
sed -n '630,690p' web/src/features/channels/components/drawers/channel-mutate-drawer.tsx
echo
sed -n '1230,1285p' web/src/features/channels/components/drawers/channel-mutate-drawer.tsx
echo
echo "===== model drawer surrounding fetch/conditionals ====="
sed -n '225,290p' web/src/features/models/components/drawers/model-mutate-drawer.tsx
echo
sed -n '390,460p' web/src/features/models/components/drawers/model-mutate-drawer.tsx
echo
sed -n '738,765p' web/src/features/models/components/drawers/model-mutate-drawer.tsx
echo "===== users drawer surrounding fetch/conditionals ====="
sed -n '105,195p' web/src/features/users/components/users-mutate-drawer.tsx
echo
sed -n '265,300p' web/src/features/users/components/users-mutate-drawer.tsx
echo
sed -n '628,645p' web/src/features/users/components/users-mutate-drawer.tsx
echo "===== error state behavior from source ====="
sed -n '1,140p' web/src/components/error-state.tsx
echo
sed -n '1,80p' web/src/components/loading-state.tsxRepository: QuantumNous/new-api
Length of output: 19675
Keep cached detail data editable after a refetch error.
TanStack Query can keep previous data on a failed background refetch while isError becomes true; these flags hide usable forms and disable saving. Only treat a query error as blocking when the required data is missing.
web/src/features/channels/components/drawers/channel-mutate-drawer.tsx#L865: require missingchannelQuery.data?.databefore settingisChannelDetailError.web/src featuring/models/components/drawers/model-mutate-drawer.tsx#L288/289: require missingmodelQuery.data?.databefore settingisModelDetailError.web/src/features/users/components/users-mutate-drawer.tsx#L179/183/186: only setdrawerLoading/drawerErrorwhen groups, permission catalog, or user details fail and have no usable cached data.
📍 Affects 3 files
web/src/features/channels/components/drawers/channel-mutate-drawer.tsx#L865-L865(this comment)web/src/features/models/components/drawers/model-mutate-drawer.tsx#L288-L289web/src/features/users/components/users-mutate-drawer.tsx#L179-L188
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/features/channels/components/drawers/channel-mutate-drawer.tsx` at
line 865, Update channel-mutate-drawer.tsx:865 so isChannelDetailError is true
only when channelQuery.isError and channelQuery.data?.data is missing. Apply the
same cached-data guard to isModelDetailError in model-mutate-drawer.tsx:288-289.
In users-mutate-drawer.tsx:179-188, set drawerLoading and drawerError only when
the groups, permission catalog, or user-details query fails without usable
cached data; preserve editing and saving when cached data remains available.
| export function requirePerformanceSummaryResponse( | ||
| response: PerfSummaryAllData | ||
| ): SuccessfulPerfSummaryData { | ||
| if (!response.success || !Array.isArray(response.data?.models)) { | ||
| throw new Error(response.message || t('Request failed') || 'Request failed') | ||
| } | ||
| return response as SuccessfulPerfSummaryData | ||
| } | ||
|
|
||
| export function requirePerformanceMetricsResponse( | ||
| response: PerformanceMetricsData | ||
| ): SuccessfulPerformanceMetricsData { | ||
| if (!response.success || !Array.isArray(response.data?.groups)) { | ||
| throw new Error(response.message || t('Request failed') || 'Request failed') | ||
| } | ||
| return response as SuccessfulPerformanceMetricsData |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline web/src/features/performance-metrics/api.ts --items all
rg -n -C 3 'Array\.isArray|as Successful|group\.series|avg_ttft_ms|success_rate' \
web/src/features/performance-metrics \
web/src/features/pricing/components/model-details-performance.tsxRepository: QuantumNous/new-api
Length of output: 10778
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- performance-metrics files ---\n'
fd -a 'types\.ts|api\.ts' web/src/features/performance-metrics
printf '\n--- api.ts ---\n'
cat -n web/src/features/performance-metrics/api.ts
printf '\n--- types.ts ---\n'
cat -n web/src/features/performance-metrics/types.ts
printf '\n--- model-details-performance relevant code ---\n'
sed -n '1,260p' web/src/features/pricing/components/model-details-performance.tsx | cat -n
printf '\n--- static probe: nested required-field usage vs assertion and array validation ---\n'
python3 - <<'PY'
from pathlib import Path
api = Path('web/src/features/performance-metrics/api.ts').read_text()
types = Path('web/src/features/performance-metrics/types.ts').read_text()
component = Path('web/src/features/pricing/components/model-details-performance.tsx').read_text()
checks = {
"api_asserts_successful_metrics": "return response as SuccessfulPerformanceMetricsData" in api,
"api_asserts_successful_summary": "return response as SuccessfulPerfSummaryData" in api,
"api_validates_groups_is_array": '!Array.isArray(response.data?.groups)' in api,
"api_validates_models_is_array": '!Array.isArray(response.data?.models)' in api,
"component_iterates_group_series": ".group.series" in component,
"component_reads_series_point_fields": all(x in component for x in ["point.ts", "point.avg_ttft_ms", "point.success_rate"]),
"component_reads_group_fields": all(x in component for x in ["group.group", "group.avg_ttft_ms", "group.success_rate"]),
"type_group_contains_required_group_fields": all(x in types for x in ["group: string", "avg_ttft_ms: number", "avg_latency_ms: number", "success_rate: number", "avg_tps: number"]),
"type_series_point_contains_required_fields": all(x in types for x in ["ts: number", "avg_ttft_ms: number", "avg_latency_ms: number", "success_rate: number", "avg_tps: number"]),
}
for name, ok in checks.items():
print(f"{name}={ok}")
PYRepository: QuantumNous/new-api
Length of output: 248
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- performance-metrics files ---'
fd 'types\.ts|api\.ts' web/src/features/performance-metrics || true
printf '%s\n' ''
printf '%s\n' '--- api.ts ---'
cat -n web/src/features/performance-metrics/api.ts
printf '%s\n' ''
printf '%s\n' '--- types.ts ---'
cat -n web/src/features/performance-metrics/types.ts
printf '%s\n' ''
printf '%s\n' '--- model-details-performance relevant code ---'
sed -n '1,260p' web/src/features/pricing/components/model-details-performance.tsx | cat -n
printf '%s\n' ''
printf '%s\n' '--- static probe: nested required-field usage vs assertion and array validation ---'
python3 - <<'PY'
from pathlib import Path
api = Path('web/src/features/performance-metrics/api.ts').read_text()
types = Path('web/src/features/performance-metrics/types.ts').read_text()
component = Path('web/src/features/pricing/components/model-details-performance.tsx').read_text()
checks = {
"api_asserts_successful_metrics": "return response as SuccessfulPerformanceMetricsData" in api,
"api_asserts_successful_summary": "return response as SuccessfulPerfSummaryData" in api,
"api_validates_groups_is_array": '!Array.isArray(response.data?.groups)' in api,
"api_validates_models_is_array": '!Array.isArray(response.data?.models)' in api,
"component_iterates_group_series": ".group.series" in component,
"component_reads_series_point_fields": all(x in component for x in ["point.ts", "point.avg_ttft_ms", "point.success_rate"]),
"component_reads_group_fields": all(x in component for x in ["group.group", "group.avg_ttft_ms", "group.success_rate"]),
"type_group_contains_required_group_fields": all(x in types for x in ["group: string", "avg_ttft_ms: number", "avg_latency_ms: number", "success_rate: number", "avg_tps: number"]),
"type_series_point_contains_required_fields": all(x in types for x in ["ts: number", "avg_ttft_ms: number", "avg_latency_ms: number", "success_rate: number", "avg_tps: number"]),
}
for name, ok in checks.items():
print(f"{name}={ok}")
PYRepository: QuantumNous/new-api
Length of output: 15809
Validate response payloads before narrowing their types.
requirePerformanceMetricsResponse() only checks response.data.groups is an array, then asserts SuccessfulPerformanceMetricsData. A payload such as { success: true, data: { groups: [{}] } } bypasses the guard, while ModelDetailsPerformance() reads group.group, group.avg_ttft_ms, and group.series fields. Validate each required field and nested item shape before the type assertion.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/features/performance-metrics/api.ts` around lines 30 - 45, Strengthen
requirePerformanceMetricsResponse by validating every required field of each
groups item, including group, avg_ttft_ms, and series, before casting to
SuccessfulPerformanceMetricsData. Reject malformed entries such as empty objects
while preserving the existing failure error behavior and valid-response path.
| } catch (fetchError) { | ||
| // eslint-disable-next-line no-console | ||
| console.error('Failed to fetch profile:', error) | ||
| console.error('Failed to fetch profile:', fetchError) | ||
| if (!silent) { | ||
| toast.error(i18next.t('Failed to load profile')) | ||
| setError(fetchError) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate files =="
fd -a 'use-profile\.ts$|profile|Profile' web/src 2>/dev/null | sed 's#^\./##' | head -200
echo
echo "== use-profile outline =="
ast-grep outline web/src/features/profile/hooks/use-profile.ts --view expanded || true
echo
echo "== use-profile relevant lines =="
wc -l web/src/features/profile/hooks/use-profile.ts
sed -n '1,220p' web/src/features/profile/hooks/use-profile.ts
echo
echo "== profile feature files outline snippets =="
for f in $(fd -e ts -e tsx '.*' web/src/features/profile | head -100); do
echo "--- $f"
ast-grep outline "$f" --view compact || true
done
echo
echo "== search Profile render and ErrorState usage =="
rg -n "ErrorState|useProfile|isLoading|failed|retry|refreshProfile|updateProfile|updateSettings" web/src/features/profile web/src -g '*.ts' -g '*.tsx' | head -300Repository: QuantumNous/new-api
Length of output: 45428
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== web/src/features/profile/index.tsx =="
sed -n '1,140p' web/src/features/profile/index.tsx
echo
echo "== web/src/features/profile/routes =="
fd -e ts -e tsx '^.*$' web/src/routes/_authenticated/profile -x sh -c 'echo "--- $1"; sed -n "1,160p" "$1"' sh {}
echo
echo "== error-state behavior =="
sed -n '1,120p' web/src/components/error-state.tsxRepository: QuantumNous/new-api
Length of output: 7943
Expose silent profile refresh failures to the user.
refreshProfile() calls fetchProfile(true), so errors are only logged and do not update error. The Profile route only renders ErrorState when !loading && error && !profile, so a failed silent refresh after an initial load is invisible and updateProfile / updateSettings can report success while still showing the stale cached profile. Store silent-refresh failures and expose a retry or stale-data recovery path, or make the silent-refresh API contract explicit if that behavior is intentional.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/features/profile/hooks/use-profile.ts` around lines 54 - 58, The
silent error path in fetchProfile currently only logs failures, leaving
refreshProfile and callers such as updateProfile/updateSettings with stale
profile data and no user-visible recovery. Update fetchProfile and
refreshProfile so silent-refresh failures are stored and exposed through the
existing profile error state, with an appropriate retry or stale-data recovery
path; otherwise make the silent-refresh contract explicit and ensure callers do
not report success when refresh fails.
| if (!res.data.success || !res.data.data) { | ||
| throw new Error(res.data.message || t('Request failed') || 'Request failed') | ||
| } | ||
| return res.data as SuccessfulRankingsResponse |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline web/src/features/rankings --items all
rg -n -C 4 '\b(getRankings|models|vendors|top_movers|top_droppers|models_history|vendor_share_history)\b' web/src/features/rankings
ast-grep outline web/src/features/pricing --items all
rg -n -C 4 '\b(getPricing|group_ratio|usable_group|supported_endpoint|auto_groups)\b' web/src/features/pricingRepository: QuantumNous/new-api
Length of output: 35455
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant API guard implementations and the exact type definitions used by consumers.
sed -n '1,80p' web/src/features/rankings/api.ts
printf '\n--- rankings/types.ts ---\n'
sed -n '1,150p' web/src/features/rankings/types.ts
printf '\n--- pricing/api.ts ---\n'
sed -n '1,80p' web/src/features/pricing/api.ts
printf '\n--- pricing/types.ts ---\n'
sed -n '1,130p' web/src/features/pricing/types.ts
# Read-only behavioral probe: show payloads that pass the current guards but are not full SuccessfulRankingsResponse/PricingData shapes.
node - <<'JS'
const payloadTypes = [
{
name: 'rankings-incomplete-empty',
passesGetRankingsGuard: (data) => data.success && data.data && data.data.success && Array.isArray(data.data.data.models) && Array.isArray(data.data.data.vendors),
data: { success: true, data: { success: true, models: [], vendors: [] } }
},
{
name: 'rankings-only-array-fields',
passesGetRankingsGuard: (data) => data.success && data.data && data.data.success && Array.isArray(data.data.data.models) && Array.isArray(data.data.data.vendors),
data: { success: true, data: { success: true, models: [], vendors: [], top_movers: [] } }
},
{
name: 'pricing-incomplete-object',
passesGetPricingGuard: (data) => data.success && Array.isArray(data.data) && Array.isArray(data.vendors),
data: { success: true, data: [], vendors: [] }
}
]
for (const p of payloadTypes) {
console.log(`${p.name}: current-guard=${p.passesGetRankingsGuard ? p.passesGetRankingsGuard(p.data) : 'n/a'} object=${typeof p.data}`)
}
JSRepository: QuantumNous/new-api
Length of output: 11334
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read-only behavioral probe: payloads that satisfy the current guards but do not satisfy the declared response contracts.
node - <<'JS'
function passesCurrentGetRankingsGuard(data) {
return data.success && data.data
}
function passesCurrentGetPricingGuard(data) {
return data.success && Array.isArray(data.data) && Array.isArray(data.vendors)
}
const incompleteRankingsSnapshot = {
success: true,
data: { success: true, models: [], vendors: [] },
}
const incompleteSuccessArray = { success: true }
const incompleteRankings = {
success: true,
data: { success: true, models: [], vendors: [], top_movers: [], top_droppers: [], models_history: { buckets: 0 }, vendor_share_history: { buckets: 0 } },
}
const insufficientPricing = { success: true, data: [], vendors: [] }
console.log(JSON.stringify({
incompleteRankingsSnapshotPassesCurrentRankingsGuard: passesCurrentGetRankingsGuard(incompleteRankingsSnapshot),
incompleteRankingsSnapshotIsRankingsSnapshot: JSON.stringify(incompleteRankingsSnapshot.data) === JSON.stringify({ success: true, models: [], vendors: [] }),
incompleteSuccessArrayPassesCurrentRankingsGuard: passesCurrentGetRankingsGuard(incompleteSuccessArray),
incompleteSuccessArrayIsRecordOrArray: (typeof incompleteSuccessArray === 'object' && incompleteSuccessArray !== null && !Array.isArray(incompleteSuccessArray)) || Array.isArray(incompleteSuccessArray),
incompleteRankingsPassesCurrentRankingsGuard: passesCurrentGetRankingsGuard(incompleteRankings),
incompleteRankingsMissingHistorySeriesValues: incompleteRankings.data.models_history.points === undefined || incompleteRankings.data.vendor_share_history.points === undefined,
insufficientPricingPassesCurrentPricingGuard: passesCurrentGetPricingGuard(insufficientPricing),
insufficientPricingMissingRequiredRecordsAndArray: insufficientPricing.group_ratio === undefined || insufficientPricing.usable_group === undefined || insufficientPricing.supported_endpoint === undefined || insufficientPricing.auto_groups === undefined,
pass: passesCurrentGetRankingsGuard(incompleteRankingsSnapshot) && passesCurrentGetPricingGuard(insufficientPricing),
}, null, 2))
JSRepository: QuantumNous/new-api
Length of output: 842
Validate every required response field before assigning the narrow return type.
The current guards let incomplete payloads pass.
web/src/features/rankings/api.ts#L42-L45:res.data.datais narrowed toSuccessfulRankingsResponse, but the guard only checks existence. RejectRankingsSnapshotwhen required fields such asmodels,vendors,top_movers,top_droppers,models_history, orvendor_share_historyare missing before returning.web/src/features/pricing/api.ts#L32-L39: the guard checksdataandvendorsarrays, butPricingDataalso requiresgroup_ratio,usable_group,supported_endpoint, andauto_groups. Check those fields before returning unchangedres.data.
📍 Affects 2 files
web/src/features/rankings/api.ts#L42-L45(this comment)web/src/features/pricing/api.ts#L32-L39
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/features/rankings/api.ts` around lines 42 - 45, Validate all required
fields before narrowing API responses: in web/src/features/rankings/api.ts lines
42-45, update the guard around the rankings response so RankingsSnapshot
requires models, vendors, top_movers, top_droppers, models_history, and
vendor_share_history; in web/src/features/pricing/api.ts lines 32-39, extend the
existing guard to require group_ratio, usable_group, supported_endpoint, and
auto_groups in addition to data and vendors, while returning the unchanged
response when valid.
| if (optionsQuery.isError) { | ||
| return ( | ||
| <SettingsPageFrame title={t(sectionMeta.titleKey)}> | ||
| <ErrorState | ||
| error={optionsQuery.error} | ||
| title={t('Failed to load')} | ||
| description={t('Please try again later.')} | ||
| onRetry={() => void optionsQuery.refetch()} | ||
| className='min-h-0 flex-1' | ||
| /> | ||
| </SettingsPageFrame> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 \
'optionsQuery\.isError|providersQuery\.isError|data === undefined' \
web/src/features/system-settings/components/settings-page.tsx \
web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsx
fd -a package.json web -d 2 -x sh -c \
'printf "\n--- %s ---\n" "$1"; rg -n "`@tanstack/react-query`" "$1"' sh {}Repository: QuantumNous/new-api
Length of output: 2003
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- settings-page query data and error handling ---'
sed -n '1,230p' web/src/features/system-settings/components/settings-page.tsx | nl -ba | sed -n '1,230p'
printf '%s\n' '--- custom-oauth query data and error handling ---'
sed -n '1,140p' web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsx | nl -ba | sed -n '1,140p'
printf '%s\n' '--- related error handling in same files ---'
rg -n -C 3 'status === '\''success'\''|useQuery|data|isError|useSuspenseQuery|ErrorState' \
web/src/features/system-settings/components/settings-page.tsx \
web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsxRepository: QuantumNous/new-api
Length of output: 249
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- settings-page query data and error handling ---'
sed -n '1,230p' web/src/features/system-settings/components/settings-page.tsx
printf '%s\n' '--- custom-oauth query data and error handling ---'
sed -n '1,140p' web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsx
printf '%s\n' '--- related error handling in same files ---'
rg -n -C 3 'status === '\''success'\''|useQuery|data|isError|useSuspenseQuery|ErrorState' \
web/src/features/system-settings/components/settings-page.tsx \
web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsxRepository: QuantumNous/new-api
Length of output: 16981
🌐 Web query:
TanStack Query v5 preserves previous data on background refetch error placeholderData isError
💡 Result:
In TanStack Query v5, placeholderData is designed to provide data only while a query is in a pending state (i.e., while it is fetching) [1][2][3]. When using placeholderData—including when using the identity function (previousData) => previousData to replicate the former keepPreviousData behavior—the placeholder data is removed as soon as the query finishes, regardless of whether it finishes successfully or with an error [4][5][3]. If the background refetch fails, the query transitions to an error state, the placeholder data is discarded, and the UI will reflect the error state (typically resulting in data becoming undefined) [4][5][6][3]. This is an intentional design choice to maintain the distinction between "real" data, which is persisted to the cache, and "placeholder" data, which is not [1][5][3]. To preserve data when a background refetch encounters an error, consider these alternatives: 1. Use initialData: Unlike placeholderData, initialData is persisted to the cache [1][3]. If you provide initialData (e.g., from the cache of another query), that data will remain available even if a background refetch fails [3]. 2. Standard Cache Behavior: TanStack Query's default behavior for stale-while-revalidate is to keep existing data in the cache until a new successful fetch replaces it [7]. If you are not changing query keys, the existing data remains available in the data property even during a failed background refetch [7]. The need for placeholderData (or the former keepPreviousData) typically arises specifically when the queryKey changes, causing the observer to treat the new key as a fresh start [8][7]. If your goal is to show an error alongside old data during a key transition, TanStack Query v5 does not provide a built-in configuration to maintain the placeholder during the error state [3]. The library assumes that once the transition to the new key is complete, the placeholder is no longer relevant [3].
Citations:
- 1: https://tanstack.com/query/v5/docs/framework/react/guides/placeholder-query-data
- 2: https://tanstack.com/query/v5/docs/framework/react/reference/useQuery
- 3: How to stop error from wiping data when using `placeholderData`. TanStack/query#5753
- 4: https://github.com/TanStack/query/blob/feb1efd8/docs/framework/react/guides/migrating-to-v5.md
- 5: RFC: Combine keepPreviousData with placeholderData TanStack/query#4426
- 6: fix(query-core): reset isPlaceholderData when select throws on placeholder data TanStack/query#11011
- 7: keepPreviousData deprecated - what now? TanStack/query#6460
- 8: https://tanstack.com/query/v5/docs/framework/react/guides/paginated-queries
🌐 Web query:
TanStack Query v5 useQuery isError data undefined stale background refetch cache
💡 Result:
In TanStack Query v5, the behavior of useQuery regarding data, errors, and cache status is governed by specific state transitions [1]. Data and Undefined State The data property defaults to undefined [2][3]. Because TanStack Query caches data, it will persist the last successfully resolved data even if a subsequent background refetch fails [2][3]. If no data has been successfully fetched yet and the query encounters an error, the data remains undefined [2][3]. In TypeScript, the data property is typed as TData | undefined, which necessitates handling the undefined case in your components [4][5]. isError and Cache Behavior When a query enters an error state (isError is true), the error property will contain the error object, and the data property will hold the most recent successfully fetched data (if any) [2][1]. TanStack Query does not automatically clear the cache when an error occurs; instead, it preserves the last known good data to allow for "stale-while-revalidate" patterns, enabling you to display stale data while showing an error indicator [2][3]. Background Refetch and Staleness By default, queries are considered stale immediately [6]. When a query is stale, TanStack Query triggers background refetches under certain conditions, such as component mounting, window refocus, or network reconnection [6]. - If a refetch is triggered in the background and fails, the query transitions to the isError state, but the previous (stale) data remains available in the data property [2][1]. - To control these refetches, you can adjust the staleTime option [6]. A higher staleTime keeps the query fresh for longer, preventing background refetches and keeping the UI consistent with the cache without unnecessary network activity [6]. Key Considerations If you encounter a scenario where a query seems stuck in an error state and refuses to refetch, ensure that the component has not suppressed retries or triggered an error boundary that requires manual reset [7][8]. The retryOnMount option (default: true) controls whether a query should attempt to refetch when a component mounts if it is currently in an error state [8]. If you wish to prevent background refetches of stale data, setting a larger staleTime or configuring refetchOnMount/refetchOnWindowFocus to false are the recommended approaches [6][9].
Citations:
- 1: https://tanstack.com/query/v5/docs/framework/react/guides/queries
- 2: https://tanstack.com/query/v5/docs/framework/react/reference/useQuery
- 3: https://tanstack.com/query/latest/docs/framework/react/reference/useQuery
- 4: https://tanstack.com/query/v5/docs/framework/react/typescript.md
- 5: useQuery & undefined data TanStack/query#1331
- 6: https://tanstack.com/query/v5/docs/framework/react/guides/important-defaults
- 7: Query caches error result and never calls queryFn TanStack/query#9728
- 8: Option to cache errors TanStack/query#1772
- 9: How can I avoid doing a refetch of an stale, but active query? TanStack/query#2018
Keep cached query data visible on an initial fetch failure.
TanStack Query preserves the last successful data when a stale background refetch fails. These branches should only render ErrorState when no cache data exists.
web/src/features/system-settings/components/settings-page.tsx: renderErrorStateonly whenoptionsQuery.isError && data === undefined.web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsx: renderErrorStateonly whenprovidersQuery.isError && providersQuery.data === undefined.
📍 Affects 2 files
web/src/features/system-settings/components/settings-page.tsx#L142-L154(this comment)web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsx#L78-L88
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/features/system-settings/components/settings-page.tsx` around lines
142 - 154, Only render the settings-page ErrorState when optionsQuery.isError
and data is undefined, preserving cached data during failed refetches; update
the corresponding condition in
web/src/features/system-settings/components/settings-page.tsx lines 142-154.
Apply the same guard to the custom OAuth ErrorState in
web/src/features/system-settings/auth/custom-oauth/custom-oauth-section.tsx
lines 78-88, requiring providersQuery.isError and providersQuery.data ===
undefined.
| !Array.isArray(resources) || | ||
| !Array.isArray(roles) | ||
| ) { | ||
| throw new Error(res.data?.message || 'Request failed') |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Localize the fallback error message.
Line 178 hardcodes Request failed. Use t('Request failed') so this fallback follows the same localization contract as the other API validators.
As per coding guidelines, “Frontend user-facing text must use i18next/react-i18next.”
Proposed fix
- throw new Error(res.data?.message || 'Request failed')
+ throw new Error(res.data?.message || t('Request failed'))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/features/users/api.ts` at line 178, Update the API error handling
around the fallback in the relevant users API validator to replace the hardcoded
“Request failed” text with the existing i18next translation function t('Request
failed'), preserving the server-provided res.data?.message when available.
Source: Coding guidelines
| const groupsQuery = useQuery({ | ||
| queryKey: ['groups'], | ||
| queryFn: getGroups, | ||
| queryFn: async () => { | ||
| const result = await getGroups() | ||
| if (!result.success || !Array.isArray(result.data)) { | ||
| throw new Error(result.message || t(ERROR_MESSAGES.LOAD_FAILED)) | ||
| } | ||
| return result.data | ||
| }, | ||
| staleTime: 5 * 60 * 1000, | ||
| }) | ||
|
|
||
| const groups = groupsData?.data || [] | ||
| const groups = groupsQuery.data ?? [] |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 "queryKey:\s*\['groups'\]|groupsData\?\.data|const groups = groupsQuery\.data" \
web/src/features/channels/components/drawers/channel-mutate-drawer.tsx \
web/src/features/users/components/users-mutate-drawer.tsxRepository: QuantumNous/new-api
Length of output: 3745
🌐 Web query:
TanStack Query same query key different shape returns cached data from another query documentation
💡 Result:
In TanStack Query, the query key is the unique identifier for your query data in the cache [1][2]. If you use the same query key for two different queries, TanStack Query will treat them as the same query and serve the cached data from the first one to the second, regardless of whether the shape of the data returned by the query functions differs [2]. This occurs because the library uses the query key to generate a deterministic hash that serves as the internal key for the query cache [1][3][2]. If multiple components or hooks use the same query key, they all subscribe to the same cache entry [4]. If the underlying query functions produce data in different formats (e.g., one returns an object and the other returns an array, or they have different properties), this will result in unexpected behavior, type errors, or runtime crashes because the cached data will not match the expected structure of the second query [2]. To avoid this, you must ensure that your query keys are unique to the specific data they represent [1][5]. A common best practice is to include all variables that affect the data result in the query key [1][2]. For example, if you have two different ways of fetching "todos," you should distinguish them in the key: // Query 1: Returns a list of todo objects useQuery({ queryKey: ['todos', 'list'], queryFn: fetchTodoList }) // Query 2: Returns a summary object useQuery({ queryKey: ['todos', 'summary'], queryFn: fetchTodoSummary }) By including unique identifiers (like strings, IDs, or configuration objects) in the query key, you ensure that TanStack Query maintains separate cache entries for distinct data shapes [1][6][5].
Citations:
- 1: https://tanstack.com/query/latest/docs/framework/react/guides/query-keys
- 2: https://tkdodo.eu/blog/effective-react-query-keys
- 3: https://tanstack-query-44.mintlify.app/concepts/query-keys
- 4: https://tanstack.com/query/latest/docs/framework/react/guides/queries
- 5: https://tanstack.com/query/v4/docs/framework/react/guides/query-keys
- 6: https://tanstack-query.mintlify.app/guides/query-keys
Use a user-scoped query key for groups.
['groups'] identifies the channel drawer query that passes through getGroups()’s API response envelope. This drawer transforms and caches only string[]. React Query can serve the incompatible cached value, and groups.map(...) can fail.
Proposed fix
- queryKey: ['groups'],
+ queryKey: ['users', 'groups'],As per coding guidelines: “每个查询必须有唯一且层级一致的数组形式 queryKey。”
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const groupsQuery = useQuery({ | |
| queryKey: ['groups'], | |
| queryFn: getGroups, | |
| queryFn: async () => { | |
| const result = await getGroups() | |
| if (!result.success || !Array.isArray(result.data)) { | |
| throw new Error(result.message || t(ERROR_MESSAGES.LOAD_FAILED)) | |
| } | |
| return result.data | |
| }, | |
| staleTime: 5 * 60 * 1000, | |
| }) | |
| const groups = groupsData?.data || [] | |
| const groups = groupsQuery.data ?? [] | |
| const groupsQuery = useQuery({ | |
| queryKey: ['users', 'groups'], | |
| queryFn: async () => { | |
| const result = await getGroups() | |
| if (!result.success || !Array.isArray(result.data)) { | |
| throw new Error(result.message || t(ERROR_MESSAGES.LOAD_FAILED)) | |
| } | |
| return result.data | |
| }, | |
| staleTime: 5 * 60 * 1000, | |
| }) | |
| const groups = groupsQuery.data ?? [] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/features/users/components/users-mutate-drawer.tsx` around lines 117 -
129, Update the groups query in the users mutate drawer to use a unique,
consistently scoped array query key that identifies this user-scoped string-list
query rather than the shared ['groups'] key. Keep the existing getGroups
response validation and groupsQuery.data fallback unchanged, and ensure the key
does not collide with other groups queries.
Source: Coding guidelines
| // eslint-disable-next-line no-console | ||
| console.error('Failed to fetch topup info:', response.message) | ||
| return | ||
| throw new Error(response.message || 'Request failed') |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Translate the fallback error message.
'Request failed' is a hard-coded fallback stored in the hook's error state. Use the project's i18n instance, such as i18next.t('Request failed'), and add the key to the locale resources if it is missing.
As per coding guidelines, front-end user-facing text must use i18n; this fallback is currently hard-coded.
Proposed fix
- throw new Error(response.message || 'Request failed')
+ throw new Error(response.message || i18next.t('Request failed'))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/features/wallet/hooks/use-topup-info.ts` at line 179, Update the
error handling in useTopupInfo so the fallback for response.message uses the
project i18n instance instead of the hard-coded “Request failed” text, and add
the translation key to locale resources if absent. Preserve response.message
when provided.
Source: Coding guidelines
Important
📝 变更描述 / Description
(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)
调整前端针对接口返回报错的行为
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
(请在此粘贴截图、关键日志或测试报告,以证明变更生效)
Summary by CodeRabbit
New Features
Bug Fixes