honojs/hono · error · JwtTokenAudience

expected audience "${Array.isArray(expected) ? expected.join

Error message

expected audience "${Array.isArray(expected) ? expected.join(', ') : expected}", got "${aud}"

What it means

Thrown when the token's aud claim does not match the expected audience. Expected can be a string, RegExp, or array; every payload audience value is tested and at least one must match.

Source

Thrown at src/utils/jwt/jwt.ts:172

      throw new JwtTokenIssuer(iss, payload.iss)
    }
  }

  if (aud) {
    if (!payload.aud) {
      throw new JwtPayloadRequiresAud(payload)
    }

    const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud]
    const matched = audiences.some((payloadAud): boolean =>
      aud instanceof RegExp
        ? aud.test(payloadAud)
        : typeof aud === 'string'
          ? payloadAud === aud
          : Array.isArray(aud) && aud.includes(payloadAud)
    )
    if (!matched) {
      throw new JwtTokenAudience(aud, payload.aud)
    }
  }

  const headerPayload = token.substring(0, token.lastIndexOf('.'))
  const verified = await verifying(
    publicKey,
    alg,
    decodeBase64Url(tokenParts[2]),
    utf8Encoder.encode(headerPayload)
  )
  if (!verified) {
    throw new JwtTokenSignatureMismatched(token)
  }

  return payload
}

// Symmetric algorithms that are not allowed for JWK verification

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Decode the token and read the actual aud value
  2. Align the configured audience with what the IdP emits (exact string)
  3. For multiple valid audiences pass an array: aud: ['api-a', 'api-b']
  4. Use a RegExp if audiences share a pattern
  5. Ensure you validate access tokens (not ID tokens) with your API's audience

Example fix

// before
await verify(token, key, { aud: 'my-api' })
// after
await verify(token, key, { aud: ['my-api', 'my-api-v2'] })
Defensive patterns

Strategy: try-catch

Validate before calling

const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud]
if (!audiences.includes(expectedAud)) console.warn('aud mismatch:', audiences)

Try / catch

try { await verify(token, key, { aud }) } catch (e) { if (e instanceof JwtTokenAudience) return unauthorized(); throw e }

Prevention

When it happens

Trigger: verify(token, key, { aud }) where aud is a string that differs from every entry in payload.aud, a RegExp that matches none, or an array that contains none of the payload's audience values.

Common situations: Client verified an ID token (aud = client_id) while expecting the API's audience; audience identifier typo; multiple APIs sharing tokens with different aud identifiers; microservice verified a token intended for another service.

Related errors


AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28). Data as JSON: /api/errors/7295075b3350687d. Report an issue: GitHub.