jlcodes99/cockpit-tools · error
403 Forbidden
Error message
403 Forbidden
What it means
The account store's quota refresh flow detects that the backend marked the account's quota as forbidden (quota.is_forbidden) and throws '403 Forbidden' so the UI can render the refresh as a failure (red cross). It is a deliberate UI-signal exception, not an HTTP-level error, though it usually mirrors a real upstream 403 from the provider.
Source
Thrown at src/stores/useAccountStore.ts:416
}
}
return {
currentAccount:
nextByTarget[target]?.id === accountId
? updatedAccount
: state.currentAccount?.id === accountId
? updatedAccount
: state.currentAccount,
currentAccountsByTarget: nextByTarget,
};
});
// 如果后端返回了配额错误信息,需要抛出异常让 UI 捕获并显示为失败(红叉)
if (updatedAccount.quota_error) {
throw new Error(updatedAccount.quota_error.message);
}
if (updatedAccount.quota?.is_forbidden) {
throw new Error("403 Forbidden");
}
} catch (e) {
// Token 级别失败(如 invalid_grant 会改变 disabled 状态):全量刷新确保数据正确
// 如果是我们自己 throw 的配额错误,因为状态已经局部更新,不再需要全量刷新
const isQuotaError = e instanceof Error && (
get().accounts.find(a => a.id === accountId)?.quota_error?.message === e.message ||
e.message === "403 Forbidden"
);
if (!isQuotaError) {
await get().fetchAccounts();
}
throw e;
} finally {
await get().fetchCurrentAccount(target);
}
},
refreshAllQuotas: async (trigger) => {View on GitHub (pinned to 1ed8b77992)
Solutions
- Catch this error in the refresh caller and surface the account as failed/forbidden in the UI
- Re-authenticate the account (update its token / re-login) so upstream 403 stops occurring
- Check the account's quota_error field for the more specific upstream reason
- Delete and re-import the account if credentials are unrecoverable
Example fix
// before
await refreshQuota(accountId); // throws '403 Forbidden' uncaught
// after
try {
await refreshQuota(accountId);
} catch (e) {
markAccountFailed(accountId, (e as Error).message); // show red cross in UI
} Defensive patterns
Strategy: try-catch
Validate before calling
const acct = get().accounts.find(a => a.id === accountId); const forbidden = acct?.quota?.is_forbidden === true || !!acct?.quota_error;
Type guard
function isQuotaForbidden(a?: { quota?: { is_forbidden?: boolean } | null }) {
return a?.quota?.is_forbidden === true;
} Try / catch
try {
await refreshQuota(accountId);
} catch (e) {
if ((e as Error).message === '403 Forbidden') {
// mark account forbidden in UI
}
} Prevention
- Check quota.is_forbidden before triggering refreshes
- Surface quota_error messages proactively in the UI
- Re-authenticate accounts whose tokens show repeated upstream failures
When it happens
Trigger: Refreshing an account's quota when the backend's updated account payload has quota.is_forbidden === true (e.g. the upstream API rejected the token with 403), or when a quota_error message is present (that branch throws first).
Common situations: Account token revoked or banned upstream; account disabled by the provider; IP/region blocked by the provider; stale credentials after password change.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- ${updatedAccount.quota_error.message}
- CODEX_PENDING_OAUTH_ACCOUNT
- [TraeAutoCheckin] 保存自动签到日志失败:
- [TraeAutoCheckin] 账号 ${account.id} 签到异常:
- [WorkbuddyAutoCheckin] 本地缓存保存失败:
AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05).
Data as JSON: /api/errors/b8b8f54a7a87c0d4.
Report an issue: GitHub.