hcengineering/platform · error · TokenError

err.message

Error message

err.message

What it means

decodeToken wraps the underlying JWT decode/verify call; any failure (bad signature, malformed token, wrong secret) is rethrown as a TokenError carrying the underlying err.message. It is the library's single funnel for token decoding failures.

Source

Thrown at foundations/core/packages/token/src/token.ts:121

      account: accountUuid,
      workspace: workspaceUuid,
      grant: sanitizedGrant,
      sub,
      exp,
      nbf
    },
    secret ?? getSecret()
  )
}

/**
 * @public
 */
export function decodeToken (token: string, verify: boolean = true, secret?: string): Token {
  try {
    return decode(token, secret ?? getSecret(), !verify)
  } catch (err: any) {
    throw new TokenError(err.message)
  }
}

/**
 * @public
 */
export function decodeTokenVerbose (ctx: MeasureContext, token: string): Token {
  try {
    return decodeToken(token)
  } catch (err: any) {
    try {
      const decode = decodeToken(token, false)
      ctx.warn('Failed to verify token', { ...decode })
    } catch (err2: any) {
      // Nothing to do
    }
    throw new TokenError(err.message)
  }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Ensure the same secret is configured on issuer and verifier (check the secret env var / metadata)
  2. Inspect the wrapped err.message — it names the underlying cause (e.g. signature mismatch, malformed JWT)
  3. Confirm the token is passed in full, without truncation or added whitespace/quotes
  4. Re-issue a fresh token if the token itself is corrupt or stale

Example fix

// before
const t = decodeToken(authHeader) // 'Bearer eyJ...' including prefix -> malformed
// after
const raw = authHeader.replace(/^Bearer\s+/i, '')
const t = decodeToken(raw)
Defensive patterns

Strategy: try-catch

Validate before calling

if (typeof token !== 'string' || token.split('.').length !== 3) {
  throw new Error('token is not a well-formed JWT (expected 3 dot-separated parts)')
}

Type guard

function looksLikeJwt(v: unknown): v is string {
  return typeof v === 'string' && /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/.test(v)
}

Try / catch

try {
  const decoded = decodeToken(token, true)
} catch (e) {
  if (e instanceof TokenError) {
    // e.message carries the underlying cause: signature, malformed, etc.
    throw new UnauthorizedError(`bad token: ${e.message}`)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling decodeToken with a token that is truncated, tampered, signed with a different secret, or not a JWT at all; or with verify=true and a mismatched secret (getSecret() result differing from the signing secret).

Common situations: Secrets differing across services/environments (JWT_SECRET env mismatch), tokens from another deployment, expired/corrupted cookies or Authorization headers, copies missing trailing characters.

Related errors


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