clockworklabs/SpacetimeDB · error · anyhow::Error
Subject empty
Error message
Subject empty
What it means
The claims conversion rejects an empty `sub` (crates/auth/src/identity.rs:113). The subject is the per-user half of the Identity hash input (Identity::from_claims(issuer, subject)); an empty subject cannot identify a principal, so the conversion fails before identity computation or the optional identity-match check at lines 117-123.
Solutions
- Require the token service to populate `sub` with the calling principal's id (any non-empty value up to 128 bytes).
- For machine identities, assign a stable synthetic subject (e.g. `svc-billing`) rather than leaving sub empty.
- Reissue tokens after fixing issuance.
Example fix
// before
{ "iss": "https://app.example.com", "sub": "" }
// after
{ "iss": "https://app.example.com", "sub": "svc-billing" } Defensive patterns
Strategy: validation
Validate before calling
// Rust: require a non-empty subject before attempting the conversion. anyhow::ensure!(!claims.subject.is_empty(), "token missing `sub` claim; refusing to derive identity");
Type guard
fn has_subject(c: &IncomingClaims) -> bool {
!c.subject.is_empty()
} Prevention
- Map every caller (human or service) to a stable non-empty subject id at token issuance.
- For machine-to-machine auth, mint synthetic subjects like `svc-<name>` instead of leaving sub blank.
- Add contract tests against your IdP's token format so claims regressions surface before production.
When it happens
Trigger: Authenticating with a JWT whose `sub` claim is missing or empty - e.g. client-credentials/machine tokens from providers that omit sub, or tokens minted with the subject field left blank.
Common situations: Service-to-service tokens from a generic OIDC provider; custom token services that authenticated the caller out-of-band and forgot to set sub; malformed tokens hand-assembled for testing.
Related errors
- Issuer empty
- Issuer too long
- Subject too long
- 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/51f5938cb854193a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/auth/src/identity.rs:118
}
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:?}",
));
}
Ok(SpacetimeIdentityClaims {
identity: computed_identity,
subject: self.subject,
issuer: self.issuer,
audience: self.audience,
iat: self.iat,View on GitHub (pinned to 3653d2ed49)