clockworklabs/SpacetimeDB · error

Subject empty

Error message

Subject empty

What it means

The subject claim must be non-empty (crates/auth/src/identity.rs:113); together with the issuer it feeds Identity::from_claims, so a missing/empty sub aborts claim conversion with this error.

Source

Thrown at crates/auth/src/identity.rs:113

}

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 524b4487d9)

Solutions

  1. Include a non-empty sub (any stable identifier up to 128 bytes) in every token
  2. For service identities, use a synthetic subject like 'service:my-service'
  3. Correct minting code that leaves sub blank and re-issue tokens

Example fix

// before (JWT payload)
{ "iss": "https://identity.example.com", "sub": "" }

// after
{ "iss": "https://identity.example.com", "sub": "service:my-service" }
Defensive patterns

Strategy: validation

Validate before calling

// Reject tokens with empty/missing sub before authentication:
const claims = decodeJwtPayload(token);
if (typeof claims.sub !== 'string' || claims.sub.length === 0) {
  throw new Error('Token rejected: sub claim missing or empty');
}

Type guard

function hasSubject(claims: Record<string, unknown>): claims is { sub: string } {
  return typeof claims.sub === 'string' && claims.sub.length > 0;
}

Prevention

When it happens

Trigger: Authenticating with a token where sub is absent or the empty string — e.g. client-credential/service tokens that identify only via other claims, or mis-minted tokens.

Common situations: Machine-to-machine tokens that omit sub; token-minting bugs; providers issuing anonymous tokens with an empty subject for unauthenticated flows.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/51f5938cb854193a. Report an issue: GitHub.