{"record":{"id":"f9b79f11c5dda115","repo":"clockworklabs/SpacetimeDB","slug":"subject-too-long","errorCode":null,"errorMessage":"Subject too long: {:?}","messagePattern":"Subject too long: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/auth/src/identity.rs","lineNumber":106,"sourceCode":"    pub iat: SystemTime,\n    #[serde_as(as = \"Option<serde_with::TimestampSeconds>\")]\n    pub exp: Option<SystemTime>,\n\n    /// All remaining claims from the JWT payload\n    #[serde(flatten)]\n    pub extra: Option<HashMap<Box<str>, serde_json::Value>>,\n}\n\nimpl TryInto<SpacetimeIdentityClaims> for IncomingClaims {\n    type Error = anyhow::Error;\n\n    fn try_into(self) -> anyhow::Result<SpacetimeIdentityClaims> {\n        // The issuer and subject must be less than 128 bytes.\n        if self.issuer.len() > 128 {\n            return Err(anyhow::anyhow!(\"Issuer too long: {:?}\", self.issuer));\n        }\n        if self.subject.len() > 128 {\n            return Err(anyhow::anyhow!(\"Subject too long: {:?}\", self.subject));\n        }\n        // The issuer and subject must be non-empty.\n        if self.issuer.is_empty() {\n            return Err(anyhow::anyhow!(\"Issuer empty\"));\n        }\n        if self.subject.is_empty() {\n            return Err(anyhow::anyhow!(\"Subject empty\"));\n        }\n\n        let computed_identity = Identity::from_claims(&self.issuer, &self.subject);\n        // If an identity is provided, it must match the computed identity.\n        if let Some(token_identity) = self.identity\n            && token_identity != computed_identity\n        {\n            return Err(anyhow::anyhow!(\n                    \"Identity mismatch: token identity {token_identity:?} does not match computed identity {computed_identity:?}\",\n                ));\n        }","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/auth/src/identity.rs#L88-L124","documentation":"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.","triggerScenarios":"Authenticating with a token whose sub exceeds 128 bytes — composite subjects like 'urn:myapp:user:12345:roles:admin,...' or subjects embedding scopes/paths.","commonSituations":"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.","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"],"exampleFix":"// before (JWT payload)\n{ \"sub\": \"urn:myapp:user:12345:roles:admin,editor:tenant:acme-west\" }\n\n// after\n{ \"sub\": \"12345\", \"roles\": [\"admin\", \"editor\"], \"tenant\": \"acme-west\" }","handlingStrategy":"validation","validationCode":"// Before sending a token to SpacetimeDB, check sub:\nfunction assertSubjectOk(jwt: { sub: string }) {\n  const bytes = new TextEncoder().encode(jwt.sub).length;\n  if (bytes === 0) throw new Error('sub must be non-empty');\n  if (bytes > 128) throw new Error(`sub is ${bytes} bytes; max is 128`);\n}","typeGuard":"function hasValidSpacetimeSubject(claims: Record<string, unknown>): claims is { sub: string } {\n  const sub = claims.sub;\n  return typeof sub === 'string' && sub.length > 0 && sub.length <= 128;\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["auth","jwt","identity","claims","validation","spacetimedb"],"backgroundTag":"jwt-subject-too-long","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}