clockworklabs/SpacetimeDB · error · anyhow::Error
Issuer empty
Error message
Issuer empty
What it means
During IncomingClaims -> SpacetimeIdentityClaims conversion (crates/auth/src/identity.rs:110), an `iss` claim that deserializes to an empty string is rejected. The issuer is one of the two inputs to Identity::from_claims, so an empty issuer would collapse many tokens onto ambiguous identities. This fires when iss is present but empty, or absent and defaulted to empty.
Solutions
- Ensure the token service always includes a non-empty `iss` claim (short canonical issuer id) in every issued JWT.
- If the token is not a JWT with proper claims, use SpacetimeDB's own token flow instead of passing it through this claims path.
- Regenerate and redistribute tokens after fixing issuance.
Example fix
// before: payload missing issuer
{ "sub": "user-1" }
// after
{ "iss": "https://app.example.com", "sub": "user-1" } Defensive patterns
Strategy: validation
Validate before calling
// Rust: require a non-empty issuer before attempting the conversion. anyhow::ensure!(!claims.issuer.is_empty(), "token missing `iss` claim; refusing to derive identity");
Type guard
fn has_issuer(c: &IncomingClaims) -> bool {
!c.issuer.is_empty()
} Prevention
- Configure the token service to always include iss; reject iss-less tokens at your ingress.
- When self-signing test tokens, start from a template that includes iss and sub.
- Distinguish JWT traffic from raw API keys at the gateway so non-JWT tokens never reach claims validation.
When it happens
Trigger: Authenticating with a JWT that omits the `iss` claim entirely, or carries `iss": ""`; tokens minted by custom services that only set `sub`; test/self-signed tokens generated without an issuer field.
Common situations: Hand-rolled token generation that never set iss; providers configured to strip iss; sending a raw API key or opaque token where a JWT is expected, so claims parse to defaults.
Related errors
- Subject 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/ad44e6e3e0834946.
Report an issue: GitHub.
Appendix: source
Thrown at crates/auth/src/identity.rs:115
/// 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:?}",
));
}
Ok(SpacetimeIdentityClaims {
identity: computed_identity,
subject: self.subject,View on GitHub (pinned to 3653d2ed49)