{"record":{"id":"d108083636fd6367","repo":"transloadit/uppy","slug":"invalid-token-payload-expected-string","errorCode":null,"errorMessage":"Invalid token payload: expected string","messagePattern":"Invalid token payload: expected string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@uppy/companion/src/server/helpers/jwt.ts","lineNumber":77,"sourceCode":"}\n\nexport const verifyEncryptedToken = (\n  token: string,\n  secret: EncryptionSecret,\n) => {\n  const ret = verifyJwtToken(decrypt(token, secret), secret)\n  if (!ret) throw new Error('No payload')\n  return ret\n}\n\nexport const verifyEncryptedAuthToken = <T extends Record<string, unknown>>(\n  token: string,\n  secret: EncryptionSecret,\n  providerName: string,\n): T => {\n  const json = verifyEncryptedToken(token, secret)\n  if (typeof json !== 'string') {\n    throw new Error('Invalid token payload: expected string')\n  }\n  const tokens: T = JSON.parse(json)\n  if (!isRecord(tokens) || !Object.hasOwn(tokens, providerName))\n    throw new Error(`Missing token payload for provider ${providerName}`)\n  return tokens\n}\n\nfunction getCommonCookieOptions({\n  companionOptions,\n}: {\n  companionOptions: CompanionRuntimeOptions\n}): Record<string, unknown> {\n  const cookieOptions: Record<string, unknown> = {\n    httpOnly: true,\n  }\n\n  // Fix to show thumbnails on Chrome\n  // https://community.transloadit.com/t/dropbox-and-box-thumbnails-returning-401-unauthorized/15781/2","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/companion/src/server/helpers/jwt.ts#L59-L95","documentation":"verifyEncryptedAuthToken expects the decrypted token payload (the `data` inside the JWT) to be a JSON string that can be parsed into a provider-token map. If the payload is present but not a string, it throws 'Invalid token payload: expected string'.","triggerScenarios":"Passing an uppy auth token whose data was stored as an object/number rather than a JSON.stringify'd string — e.g. custom code called generateEncryptedToken(obj, secret) instead of generateEncryptedToken(JSON.stringify(obj), secret).","commonSituations":"Hand-rolled token generation that skips the stringify step, or tokens produced by a differently-implemented helper in another service sharing the secret.","solutions":["Generate tokens with the provided helpers so the payload is JSON.stringify'd","If generating manually: generateEncryptedToken(JSON.stringify(tokens), secret)","Regenerate affected tokens after fixing the producer"],"exampleFix":"// before\nconst token = generateEncryptedToken(tokens, secret) // object payload\n\n// after\nconst token = generateEncryptedToken(JSON.stringify(tokens), secret)","handlingStrategy":"type-guard","validationCode":"const token = generateEncryptedToken(JSON.stringify(tokens), secret)","typeGuard":"const isJsonString = (v: unknown): v is string => typeof v === 'string';","tryCatchPattern":"try { verifyEncryptedAuthToken(token, secret, 'dropbox') } catch (err) {\n  if (err.message === 'Invalid token payload: expected string') { /* re-mint token with stringified payload */ }\n}","preventionTips":["Always JSON.stringify before generateEncryptedToken for auth tokens","Use verify/generate helpers as a matched pair"],"tags":["companion","jwt","token","auth"],"backgroundTag":"jwt-payload-shape-invalid","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}