hcengineering/platform · error · PlatformError

platform.status.BadRequest

platform.status.BadRequest

Error message

BadRequest

What it means

After resolving the account, createAccessLink requires the decoded token to carry a workspace UUID. When decodeTokenVerbose returns workspace == null (token has no workspace claim), the platform throws BadRequest. It signals a malformed token rather than missing data.

Source

Thrown at server/account/src/operations.ts:770

    extra?: string
    navigateUrl?: string
    spaces?: string[]

    notBefore?: number
    expiration?: number
    personalized?: boolean
  }
): Promise<string> {
  const { role, firstName, lastName, navigateUrl, spaces, notBefore, expiration, personalized = true } = params
  const { account, workspace: workspaceUuid, extra } = decodeTokenVerbose(ctx, token)

  const currentAccount = await db.account.findOne({ uuid: account })
  if (currentAccount == null) {
    throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account }))
  }

  if (workspaceUuid == null) {
    throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
  }

  const workspace = await db.workspace.findOne({ uuid: workspaceUuid })
  if (workspace == null) {
    throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspaceUuid }))
  }

  let extraObj: Record<string, string> | undefined

  if (params.extra != null) {
    try {
      extraObj = JSON.parse(params.extra)
    } catch (e) {
      ctx.error("Invalid extra parameter, couldn't parse JSON", { extra: params.extra })
      throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
    }
  }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Mint the token in a workspace context (after the user selects/joins a workspace) before calling createAccessLink.
  2. Decode the token locally and check the workspace claim is present before the call.
  3. Upgrade/align client and server versions so token minting includes the workspace claim.

Example fix

// before: token without workspace claim
const link = await createAccessLink(noWorkspaceToken, params)
// after: ensure token carries workspace
const payload = decodeJwt(noWorkspaceToken)
if (payload.workspace == null) throw new Error('token has no workspace; re-authenticate')
const link = await createAccessLink(noWorkspaceToken, params)
Defensive patterns

Strategy: validation

Validate before calling

const payload = decodeJwt(token)
if (payload?.workspace == null) {
  throw new Error('Token has no workspace claim; authenticate within a workspace context first')
}

Type guard

function hasWorkspaceClaim(t: unknown): t is { account: string; workspace: string } {
  return typeof t === 'object' && t !== null &&
    typeof (t as any).workspace === 'string' && (t as any).workspace.length > 0
}

Prevention

When it happens

Trigger: Calling createAccessLink with a token that lacks a workspace claim — e.g. a token minted without a selected workspace, a system/anonymous token, or a truncated token payload.

Common situations: Using pre-workspace-selection tokens (right after signup before joining a workspace); tokens issued by older service versions without the workspace field; hand-built test tokens omitting claims.

Related errors


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