hcengineering/platform · error
Missing account, cannot update plan
Error message
Missing account, cannot update plan
What it means
HTTP 400 returned when neither subscription.accountUuid nor req.token?.account yields an account UUID (services/payment/pod-payment/src/server.ts:415-418). provider.updateSubscriptionPlan requires the accountUuid to attribute the plan change, but the stored subscription has no accountUuid and the auth token carries no account claim.
Source
Thrown at services/payment/pod-payment/src/server.ts:417
return
}
if (loginInfo?.workspaceUrl === undefined) {
res.status(401).json({ error: 'Missing workspace url in login info' })
return
}
// Get subscription from our database using internal ID
const subscription = await accountClient.getSubscriptionById(subscriptionId)
if (subscription === undefined || subscription === null) {
res.status(404).json({ error: 'Subscription not found' })
return
}
const accountUuid = subscription.accountUuid ?? req.token?.account
if (accountUuid == null) {
res.status(400).json({ error: 'Missing account, cannot update plan' })
return
}
let updateResult: SubscriptionData | CheckoutResponse | null
try {
// Update via provider using the provider's subscription ID
updateResult = await provider.updateSubscriptionPlan(
ctx,
subscription.providerSubscriptionId,
plan,
loginInfo.workspaceUrl,
accountUuid
)
} catch (err) {
ctx.error('Failed to update subscription at provider', { err })
res.status(500).json({ error: 'Failed to update subscription at provider' })
returnView on GitHub (pinned to 63e28dc964)
Solutions
- Use a token that includes the account claim (re-login / re-mint the token) so req.token.account is populated.
- Backfill subscription.accountUuid in the accounts database for the affected subscription.
- Fix the signup/checkout flow so subscriptions are stored with accountUuid set.
- Verify the token's account claim is correctly parsed by withToken (claim name mismatch).
Example fix
// before: legacy row with no linkage UPDATE subscription SET account_uuid = NULL WHERE ... -- problem persists // after: backfill account linkage UPDATE subscription SET account_uuid = '<account-uuid>' WHERE id = '<subscription-id>';
Defensive patterns
Strategy: validation
Validate before calling
const sub = await getSubscription(id)
const accountUuid = sub.accountUuid ?? tokenClaims.account
if (!accountUuid) {
throw new DataError('No account linkage on subscription and no account claim in token; backfill or re-authenticate')
} Type guard
function hasAccount(sub: { accountUuid?: string | null }, token?: { account?: string }): sub is { accountUuid: string } {
return Boolean(sub.accountUuid ?? token?.account)
} Try / catch
try {
return await updatePlan(id, plan)
} catch (err) {
if (err instanceof HttpError && err.status === 400 && /missing account/i.test(err.message)) {
const token = await reauthenticateWithAccountScope()
return updatePlan(id, plan, token)
}
throw err
} Prevention
- Backfill accountUuid on legacy subscription rows during migrations.
- Ensure the checkout flow always stores accountUuid at creation time.
- Use tokens minted after account creation so the account claim exists.
- Add a DB constraint/check flagging subscriptions without account linkage.
When it happens
Trigger: POST .../updatePlan on a subscription row missing accountUuid (legacy/pre-migration rows, or rows created outside normal signup) AND a token whose payload has no account claim — e.g. a narrowly-scoped or malformed token.
Common situations: Subscriptions created before the accountUuid field existed; subscriptions synced from provider without account linkage; tokens issued by a legacy auth flow lacking the account claim; tokens for accounts deleted mid-session.
Related errors
- Couldn't find workspace with the provided token
- Missing or invalid field: plan
- Failed to load server config
- getDisplayMedia not supported
- No screen access granted
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/304159f1be3acae6.
Report an issue: GitHub.