clockworklabs/SpacetimeDB · error · anyhow::Error
Subject too long: {:?}
Error message
Subject too long: {:?} What it means
During JWT claim validation (crates/auth/src/identity.rs:106), the sub (subject) claim must be at most 128 bytes, mirroring the issuer limit, because both feed the fixed-size Identity hash. Longer subjects fail with this error before any identity is computed.
Source
Thrown at crates/auth/src/identity.rs:106
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!(
"Identity mismatch: token identity {token_identity:?} does not match computed identity {computed_identity:?}",
));
}View on GitHub (pinned to 524b4487d9)
Solutions
- Emit a compact subject (UUID, numeric id, or short opaque handle) of at most 128 bytes
- Move roles/scopes/metadata out of sub into dedicated claims
- If the provider cannot change sub, introduce a proxy/token-minting layer that rewrites sub before the token reaches SpacetimeDB
Example fix
// before (JWT payload)
{ "sub": "urn:myapp:user:12345:roles:admin,editor:tenant:acme-west" }
// after
{ "sub": "12345", "roles": ["admin", "editor"], "tenant": "acme-west" } Defensive patterns
Strategy: validation
Validate before calling
// Before sending a token to SpacetimeDB, check sub:
function assertSubjectOk(jwt: { sub: string }) {
const bytes = new TextEncoder().encode(jwt.sub).length;
if (bytes === 0) throw new Error('sub must be non-empty');
if (bytes > 128) throw new Error(`sub is ${bytes} bytes; max is 128`);
} Type guard
function hasValidSpacetimeSubject(claims: Record<string, unknown>): claims is { sub: string } {
const sub = claims.sub;
return typeof sub === 'string' && sub.length > 0 && sub.length <= 128;
} Prevention
- Use compact opaque subjects (UUIDs/ids) in sub
- Put roles and metadata in dedicated claims, not sub
- Add claim-length assertions to token-minting unit tests
When it happens
Trigger: Authenticating with a token whose sub exceeds 128 bytes — composite subjects like 'urn:myapp:user:12345:roles:admin,...' or subjects embedding scopes/paths.
Common situations: Providers that use URN-style or multi-part subjects; internal token minting that concatenates user id plus metadata into sub; migrating from a provider with short UUIDs to one with verbose subjects.
Related errors
- Issuer too long: {:?}
- Issuer empty
- Subject empty
- Identity mismatch: token identity {token_identity:?} does no
- No fingerprint saved for server: {}
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/f9b79f11c5dda115.
Report an issue: GitHub.