{"record":{"id":"081af807d3dc5a73","repo":"clockworklabs/SpacetimeDB","slug":"error-getting-jwt-errno","errorCode":null,"errorMessage":"Error getting jwt: {errno}","messagePattern":"Error getting jwt: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bindings-sys/src/lib.rs","lineNumber":1486,"sourceCode":"        raw::identity(buf.as_mut_ptr());\n    }\n    buf\n}\n\n/// Finds the JWT payload associated with `connection_id`.\n/// If nothing is found for the connection, this returns None.\n/// If a payload is found, this will return a valid [`raw::BytesSource`].\n///\n/// This must be called inside a transaction (because it reads from a system table).\n///\n/// # Errors\n///\n/// This panics on any error. You can see details about errors in [`raw::get_jwt`].\n#[inline]\npub fn get_jwt(connection_id: [u8; 16]) -> Option<raw::BytesSource> {\n    let source = unsafe {\n        call(|out| raw::get_jwt(connection_id.as_ptr(), out))\n            .unwrap_or_else(|errno: Errno| panic!(\"Error getting jwt: {errno}\"))\n    };\n\n    if source == raw::BytesSource::INVALID {\n        None // No JWT found.\n    } else {\n        Some(source)\n    }\n}\n\npub struct RowIter {\n    raw: raw::RowIter,\n}\n\nimpl RowIter {\n    /// Read some number of BSATN-encoded rows into the provided buffer.\n    ///\n    /// Returns the number of new bytes added to the end of the buffer.\n    /// When the iterator has been exhausted,","sourceCodeStart":1468,"sourceCodeEnd":1504,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/bindings-sys/src/lib.rs#L1468-L1504","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Move the get_jwt call inside a #[reducer] function - reducers always run in a transaction.","Take the connection id from the current call's ReducerContext (ctx.connection_id()) rather than storing one from an earlier invocation.","Handle the None return for connections that did not authenticate with a JWT instead of assuming a payload."],"exampleFix":"// before: called outside a transaction\nfn maybe_forward(ctx_id: ConnectionId) {\n    let jwt = spacetimedb::get_jwt(ctx_id); // panics: NOT_IN_TRANSACTION\n}\n\n// after: inside a reducer, using the live context\n#[spacetimedb::reducer]\nfn forward_jwt(ctx: &ReducerContext) {\n    if let Some(jwt) = spacetimedb::get_jwt(ctx.connection_id()) {\n        // use jwt payload\n    }\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only call get_jwt inside a #[reducer] - reducers are the only context with a transaction.","Always source the ConnectionId from ReducerContext::connection_id() of the current call, never a stored one.","Treat None as a normal result: not every connection authenticated with a JWT."],"tags":["rust","wasm","jwt","transaction","host-call"],"backgroundTag":"jwt-retrieval-failed","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}