clockworklabs/SpacetimeDB · error · anyhow::Error
Subject too long
Error message
Subject too long: {:?} What it means
The same IncomingClaims -> SpacetimeIdentityClaims conversion enforces a 128-byte cap on the `sub` claim (crates/auth/src/identity.rs:106). Subject and issuer are hashed together into the Identity, so both are bounded. An oversized subject is rejected with its debug form in the message.
Solutions
- Configure the provider/token service to emit a compact subject (opaque user id) and move extra data to other claims.
- If subjects are fixed by the provider, introduce a token-exchange step that rewrites sub to a short id before the token reaches SpacetimeDB.
- Reissue tokens after the change; identities derived from the old subject will not match the new ones.
Example fix
// before
{ "iss": "https://app.example.com", "sub": "urn:example:tenant:1234567890:user:abcdef01-2345-6789-abcd-ef0123456789:session:..." }
// after
{ "iss": "https://app.example.com", "sub": "user_abcdef01", "tid": "1234567890" } Defensive patterns
Strategy: validation
Validate before calling
// Rust: validate subject size before converting.
if claims.subject.len() > 128 {
return Err(anyhow!("sub too long ({} bytes, max 128); use a compact subject id", claims.subject.len()));
} Type guard
fn subject_within_limit(c: &IncomingClaims) -> bool {
(1..=128).contains(&c.subject.len())
} Prevention
- Emit opaque short subject ids from your identity provider or token exchange.
- Never encode composite metadata (tenant + session + scopes) into sub; use separate claims.
- Log claim sizes (not values) at issuance in CI to catch provider format changes early.
When it happens
Trigger: Authenticating with a JWT whose `sub` exceeds 128 bytes - e.g. providers that emit composite subjects (user-id + tenant + session concatenated), full UUID URNs with long prefixes, or identifiers carrying encoded metadata.
Common situations: Enterprise IdPs with verbose subject formats; token services that encode scopes or tenant info into sub instead of separate claims; testing with synthetic oversized identifiers.
Related errors
- Issuer 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/f9b79f11c5dda115.
Report an issue: GitHub.
Appendix: source
Thrown at crates/auth/src/identity.rs:111
#[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!(
"Identity mismatch: token identity {token_identity:?} does not match computed identity {computed_identity:?}",
));
}View on GitHub (pinned to 3653d2ed49)