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' })
            return

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Use a token that includes the account claim (re-login / re-mint the token) so req.token.account is populated.
  2. Backfill subscription.accountUuid in the accounts database for the affected subscription.
  3. Fix the signup/checkout flow so subscriptions are stored with accountUuid set.
  4. 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

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


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/304159f1be3acae6. Report an issue: GitHub.