{"record":{"id":"c1cab20a218e1f8c","repo":"jlcodes99/cockpit-tools","slug":"updatedaccount-quota-error-message","errorCode":null,"errorMessage":"${updatedAccount.quota_error.message}","messagePattern":"\\$\\{updatedAccount\\.quota_error\\.message\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/stores/useAccountStore.ts","lineNumber":413,"sourceCode":"                            currentTarget,\n                            updatedAccount,\n                        );\n                    }\n                }\n                return {\n                    currentAccount:\n                        nextByTarget[target]?.id === accountId\n                            ? updatedAccount\n                            : state.currentAccount?.id === accountId\n                              ? updatedAccount\n                              : state.currentAccount,\n                    currentAccountsByTarget: nextByTarget,\n                };\n            });\n\n            // 如果后端返回了配额错误信息，需要抛出异常让 UI 捕获并显示为失败（红叉）\n            if (updatedAccount.quota_error) {\n                throw new Error(updatedAccount.quota_error.message);\n            }\n            if (updatedAccount.quota?.is_forbidden) {\n                throw new Error(\"403 Forbidden\");\n            }\n        } catch (e) {\n            // Token 级别失败（如 invalid_grant 会改变 disabled 状态）：全量刷新确保数据正确\n            // 如果是我们自己 throw 的配额错误，因为状态已经局部更新，不再需要全量刷新\n            const isQuotaError = e instanceof Error && (\n                get().accounts.find(a => a.id === accountId)?.quota_error?.message === e.message ||\n                e.message === \"403 Forbidden\"\n            );\n            if (!isQuotaError) {\n                await get().fetchAccounts();\n            }\n            throw e;\n        } finally {\n            await get().fetchCurrentAccount(target);\n        }","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/stores/useAccountStore.ts#L395-L431","documentation":"During a token-refresh flow in useAccountStore, the backend attaches a quota_error to the updated account. The store deliberately re-throws the backend-provided message after committing state, so the UI can render the account as failed (red X). The thrown message is dynamic — it is whatever string the backend put in quota_error.message (e.g. quota exhausted, rate limited, account suspended).","triggerScenarios":"Any refresh/switch operation on an account where the backend response's updatedAccount.quota_error is set — typically when the provider reports the account hit its quota or was rejected during quota check.","commonSituations":"Free-tier accounts exhausting daily quotas, provider returning 429/quota errors en masse, or an account flagged/suspended upstream; users see the red X with the backend's message text.","solutions":["Read the thrown message — it is the provider's own quota explanation and dictates the fix","Wait for the quota window to reset or switch to a paid/higher-tier plan for that provider","Disable or remove the affected account so it is not retried in rotation","Check the account's quota.is_forbidden status; if forbidden, re-auth or replace the account"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (updatedAccount.quota_error) {\n  console.warn(`Account ${updatedAccount.id} has quota error: ${updatedAccount.quota_error.message}`);\n}","typeGuard":"function hasQuotaError(a: { quota_error?: { message: string } | null }): a is { quota_error: { message: string } } {\n  return Boolean(a.quota_error?.message);\n}","tryCatchPattern":"try {\n  await refreshAccountToken(accountId);\n} catch (e) {\n  markAccountFailed(accountId, e.message); // render red X with backend quota message\n  if (String(e.message) === '403 Forbidden') suggestReauth(accountId);\n}","preventionTips":["Surface quota_error messages directly in the account list UI","Monitor quota.is_forbidden and auto-disable forbidden accounts","Rotate to backup accounts before quotas exhaust","Catch token-level failures and trigger a full account refresh"],"tags":["quota","account-store","backend","ui-state"],"backgroundTag":"provider-quota-exceeded","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}