{"record":{"id":"2fdd80755ab3aac7","repo":"clockworklabs/SpacetimeDB","slug":"error-reading-environment-errno","errorCode":null,"errorMessage":"Error reading environment: {errno}","messagePattern":"Error reading environment: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bindings-sys/src/lib.rs","lineNumber":1511,"sourceCode":"#[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\n/// Read a database environment value without exposing the system table.\n#[inline]\npub fn env_get(key: &str) -> Option<raw::BytesSource> {\n    let source = unsafe { call(|out| raw::env_get(key.as_ptr(), key.len(), out)) }\n        .unwrap_or_else(|errno: Errno| panic!(\"Error reading environment: {errno}\"));\n    (source != raw::BytesSource::INVALID).then_some(source)\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,\n    /// `self.is_exhausted()` will return `true`.\n    pub fn read(&mut self, buf: &mut Vec<u8>) -> usize {\n        loop {\n            let buf_ptr = buf.spare_capacity_mut();\n            let mut buf_len = buf_ptr.len();\n            let ret = unsafe { raw::row_iter_bsatn_advance(self.raw, buf_ptr.as_mut_ptr().cast(), &mut buf_len) };","sourceCodeStart":1493,"sourceCodeEnd":1529,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/eddf9f5014579a50d4b67630e28b6e15cad9c4af/crates/bindings-sys/src/lib.rs#L1493-L1529","documentation":"env_get reads a database environment variable via the FFI binding raw::env_get without exposing the system table. If the underlying host call returns a non-zero errno, the function panics with 'Error reading environment: {errno}' instead of returning an Option. This is an unrecoverable host/FFI failure, distinct from the key simply being absent (which yields None).","triggerScenarios":"Calling spacetimedb::env_get(key) when the FFI call raw::env_get returns an Errno (e.g. malformed key pointer/length, host not providing the env table, or host-side internal failure).","commonSituations":"Calling env_get inside a reducer against a host runtime that lacks an environment table, or with a key that the host rejects at the FFI boundary; also seen when running module code outside a proper host (tests, mocked runtimes).","solutions":["Verify the key is a valid environment variable name (no control chars, non-empty) before calling env_get.","Confirm the code runs inside a real SpacetimeDB host that supplies st_env, not a bare test harness.","Check the errno value in the panic message against the Errno definitions to identify the host-side failure.","If the key may be legitimately absent, rely on the None return instead of treating absence as an FFI error."],"exampleFix":"// before\nlet v = env_get(\"PORT\").unwrap_or_default();\n// after\nlet v = match std::panic::catch_unwind(|| env_get(\"PORT\")) {\n    Ok(Some(src)) => src,\n    _ => Default::default(),\n};","handlingStrategy":"try-catch","validationCode":"fn valid_env_key(k: &str) -> bool { !k.is_empty() && k.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') }","typeGuard":"fn checked_env_get(k: &str) -> Option<raw::BytesSource> { assert!(valid_env_key(k)); std::panic::catch_unwind(|| env_get(k)).ok().flatten() }","tryCatchPattern":"let value = std::panic::catch_unwind(|| env_get(key)).ok().flatten();","preventionTips":["Validate key names before calling env_get","Only call env_get inside a real SpacetimeDB host runtime","Map Errno values to actionable log messages in host tooling"],"tags":["ffi","panic","environment","rust"],"backgroundTag":"missing-env-var","analyzedSha":"eddf9f5014579a50d4b67630e28b6e15cad9c4af","analyzedAt":"2026-09-20T12:15:59.611Z","contentChangedAt":"2026-09-20T12:15:59.611Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}