clockworklabs/SpacetimeDB · error · anyhow::Error
Issuer too long
Error message
Issuer too long: {:?} What it means
When an incoming JWT is converted to SpacetimeIdentityClaims (crates/auth/src/identity.rs:103), the `iss` claim is capped at 128 bytes because issuer and subject are the two inputs hashed into the fixed-size Identity. A longer issuer is rejected before any identity computation. The debug form of the oversized issuer is included in the error.
Solutions
- Shorten the issuer: use the provider's canonical short issuer URL (or a compact custom iss value) and reissue tokens.
- If you control the token service, keep `iss` a short stable identifier and move metadata to other claims (they are preserved in `extra`).
- Re-issue and re-distribute tokens after changing the issuer, since Identity is derived from iss+sub.
Example fix
// before: JWT payload
{ "iss": "https://long-tenant-name.region.identity.example.com/v2/tenants/1234567890/delegation", "sub": "user-1" }
// after
{ "iss": "https://app.example.com", "sub": "user-1" } Defensive patterns
Strategy: validation
Validate before calling
// Rust: validate claim sizes before converting.
if claims.issuer.len() > 128 {
return Err(anyhow!("iss too long ({} bytes, max 128); reissue with a short issuer", claims.issuer.len()));
} Type guard
fn issuer_within_limit(c: &IncomingClaims) -> bool {
(1..=128).contains(&c.issuer.len())
} Prevention
- At your token service, enforce short canonical `iss` values (simple domain-style identifiers).
- Keep multi-tenant or region metadata in custom claims, not in iss.
- Add a test asserting issued tokens keep iss and sub under 128 bytes.
When it happens
Trigger: Authenticating with a JWT whose `iss` claim exceeds 128 bytes - typically identity providers that embed long tenant/instance paths in the issuer URL, or custom token services that put extra data into iss.
Common situations: Switching an OIDC provider or tenant whose issuer URL is long (multi-segment Azure AD / Auth0 tenant domains with region prefixes); hand-built token services stuffing parameters into iss; migrating from one provider to another without re-checking claim sizes.
Related errors
- Subject too long
- Issuer empty
- Subject empty
- Invalid JSON: failed to parse string
- Argument must be a Identity
AI-assisted analysis of clockworklabs/SpacetimeDB@3653d2ed49 (2026-08-20).
Data as JSON: /api/errors/afa358d4e85a5f62.
Report an issue: GitHub.
Appendix: source
Thrown at crates/auth/src/identity.rs:108
#[serde_as(as = "serde_with::TimestampSeconds")]
pub iat: SystemTime,
// See `SpacetimeIdentityClaims::exp`: absent and null both mean no expiration.
#[serde_as(as = "Option<serde_with::TimestampSeconds>")]
#[serde(skip_serializing_if = "Option::is_none")]
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 3653d2ed49)