clockworklabs/SpacetimeDB · error · anyhow::Error

Issuer too long: {:?}

Error message

Issuer too long: {:?}

What it means

When converting incoming JWT claims into SpacetimeIdentityClaims (crates/auth/src/identity.rs:103), SpacetimeDB requires the iss claim to be at most 128 bytes. The identity is derived by hashing iss+sub into a fixed-size Identity, so oversized issuers are rejected outright.

Source

Thrown at crates/auth/src/identity.rs:103

    /// The unix timestamp the token was issued at
    #[serde_as(as = "serde_with::TimestampSeconds")]
    pub iat: SystemTime,
    #[serde_as(as = "Option<serde_with::TimestampSeconds>")]
    pub exp: Option<SystemTime>,

    /// All remaining claims from the JWT payload
    #[serde(flatten)]
    pub extra: Option<HashMap<Box<str>, serde_json::Value>>,
}

impl TryInto<SpacetimeIdentityClaims> for IncomingClaims {
    type Error = anyhow::Error;

    fn try_into(self) -> anyhow::Result<SpacetimeIdentityClaims> {
        // The issuer and subject must be less than 128 bytes.
        if self.issuer.len() > 128 {
            return Err(anyhow::anyhow!("Issuer too long: {:?}", self.issuer));
        }
        if self.subject.len() > 128 {
            return Err(anyhow::anyhow!("Subject too long: {:?}", self.subject));
        }
        // The issuer and subject must be non-empty.
        if self.issuer.is_empty() {
            return Err(anyhow::anyhow!("Issuer empty"));
        }
        if self.subject.is_empty() {
            return Err(anyhow::anyhow!("Subject empty"));
        }

        let computed_identity = Identity::from_claims(&self.issuer, &self.subject);
        // If an identity is provided, it must match the computed identity.
        if let Some(token_identity) = self.identity
            && token_identity != computed_identity
        {
            return Err(anyhow::anyhow!(

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Shorten the issuer string to 128 bytes or fewer (compact identifier or bare URL without query parameters)
  2. Reconfigure the identity provider to emit the short form of its issuer
  3. If you mint tokens yourself, keep iss minimal and put extra metadata in custom claims

Example fix

// before (JWT payload)
{ "iss": "https://identity.internal.example.com/realms/master/protocol/openid-connect/?env=prod", ... }

// after
{ "iss": "https://identity.example.com/master", ... }
Defensive patterns

Strategy: validation

Validate before calling

// Before sending a token to SpacetimeDB, check iss:
function assertIssuerOk(jwt: { iss: string }) {
  const bytes = new TextEncoder().encode(jwt.iss).length;
  if (bytes === 0) throw new Error('iss must be non-empty');
  if (bytes > 128) throw new Error(`iss is ${bytes} bytes; max is 128`);
}

Type guard

function hasValidSpacetimeIssuer(claims: Record<string, unknown>): claims is { iss: string } {
  const iss = claims.iss;
  return typeof iss === 'string' && iss.length > 0 && iss.length <= 128;
}

Prevention

When it happens

Trigger: Authenticating with a token whose iss claim exceeds 128 bytes — e.g. a long OIDC issuer URL with paths, ports, and query strings — during token exchange / spacetime login against a custom identity provider.

Common situations: Custom JWT providers (Auth0, Keycloak, self-hosted) configured with verbose issuer URLs; tokens minted by internal tooling that stuffs parameters into iss; issuer URLs that grew when moving environments or regions.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/afa358d4e85a5f62. Report an issue: GitHub.