clockworklabs/SpacetimeDB · error

Error getting jwt: {errno}

Error message

Error getting jwt: {errno}

What it means

bindings-sys get_jwt panics when the host returns an errno for the get_jwt syscall instead of a payload. Per the raw ABI the only documented errno is NOT_IN_TRANSACTION: reading the JWT touches a system table, so it must run inside a transaction (i.e. inside a reducer call). A connection with no JWT is NOT an error - that case returns None (INVALID BytesSource).

Source

Thrown at crates/bindings-sys/src/lib.rs:1486

        raw::identity(buf.as_mut_ptr());
    }
    buf
}

/// Finds the JWT payload associated with `connection_id`.
/// If nothing is found for the connection, this returns None.
/// If a payload is found, this will return a valid [`raw::BytesSource`].
///
/// This must be called inside a transaction (because it reads from a system table).
///
/// # Errors
///
/// This panics on any error. You can see details about errors in [`raw::get_jwt`].
#[inline]
pub fn get_jwt(connection_id: [u8; 16]) -> Option<raw::BytesSource> {
    let source = unsafe {
        call(|out| raw::get_jwt(connection_id.as_ptr(), out))
            .unwrap_or_else(|errno: Errno| panic!("Error getting jwt: {errno}"))
    };

    if source == raw::BytesSource::INVALID {
        None // No JWT found.
    } else {
        Some(source)
    }
}

pub struct RowIter {
    raw: raw::RowIter,
}

impl RowIter {
    /// Read some number of BSATN-encoded rows into the provided buffer.
    ///
    /// Returns the number of new bytes added to the end of the buffer.
    /// When the iterator has been exhausted,

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. Move the get_jwt call inside a #[reducer] function - reducers always run in a transaction.
  2. Take the connection id from the current call's ReducerContext (ctx.connection_id()) rather than storing one from an earlier invocation.
  3. Handle the None return for connections that did not authenticate with a JWT instead of assuming a payload.

Example fix

// before: called outside a transaction
fn maybe_forward(ctx_id: ConnectionId) {
    let jwt = spacetimedb::get_jwt(ctx_id); // panics: NOT_IN_TRANSACTION
}

// after: inside a reducer, using the live context
#[spacetimedb::reducer]
fn forward_jwt(ctx: &ReducerContext) {
    if let Some(jwt) = spacetimedb::get_jwt(ctx.connection_id()) {
        // use jwt payload
    }
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Calling get_jwt(connection_id) outside a reducer transaction, e.g. from an HTTP handler, from module initialization code, or from a helper invoked after the reducer's transaction ended; using a ConnectionId captured in a previous call.

Common situations: Refactoring external-authentication forwarding (calling a third-party API with the caller's JWT) out of a reducer into an HTTP handler; testing JWT plumbing in a plain unit function rather than a reducer.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/081af807d3dc5a73. Report an issue: GitHub.