clockworklabs/SpacetimeDB · error
Failed to BSATN-deserialize `{}`: {err:#?}
Error message
Failed to BSATN-deserialize `{}`: {err:#?} What it means
read_bytes_source_as drains a BytesSource (scheduled-reducer arguments, HTTP body bytes, etc.) into a buffer and BSATN-decodes it as T, panicking with T's type name and the debug error when decoding fails. A decode failure means the bytes were not produced by the schema T expects - a producer/consumer schema mismatch, not corrupted memory.
Source
Thrown at crates/bindings/src/rt.rs:1429
_reducer: R,
args: A,
) {
let arg_bytes = bsatn::to_vec(&SerDeArgs(args)).unwrap();
// Schedule the reducer.
sys::volatile_nonatomic_schedule_immediate(R2::NAME, &arg_bytes)
}
/// Read `source` completely into a temporary buffer, then BSATN-deserialize it as a `T`.
///
/// Panics if the bytes from `source` fail to deserialize as `T`.
/// The type name of `T` will be included in the panic message.
#[cfg_attr(not(feature = "unstable"), allow(unused))]
pub(crate) fn read_bytes_source_as<T: DeserializeOwned + 'static>(source: BytesSource) -> T {
let mut buf = IterBuf::take();
read_bytes_source_into(source, &mut buf);
bsatn::from_slice::<T>(&buf)
.unwrap_or_else(|err| panic!("Failed to BSATN-deserialize `{}`: {err:#?}", std::any::type_name::<T>()))
}
pub trait ExplicitNames {
fn explicit_names() -> RawExplicitNames {
RawExplicitNames::default()
}
}
View on GitHub (pinned to 6dee26c6ef)
Solutions
- Make the reader's type identical to the writer's type and rebuild/redeploy both together.
- If a scheduled reducer's args type changed, clear or migrate outstanding scheduled rows before invoking the new code.
- Regenerate client bindings so request/response types match the module's current schema.
Example fix
// before: scheduled args type changed, old rows still decode as the old shape
#[spacetimedb::scheduled(reducer = tick)]
pub struct Tick { pub retries: u32 }
// after: keep scheduled row layouts compatible or clear stale rows before deploying
spacetime call my-db delete_all_scheduled_tick // or migrate in `init`/migration reducer Defensive patterns
Strategy: validation
Prevention
- Change scheduled reducer argument types only alongside a migration that clears or rewrites stored rows.
- Deploy writers and readers of shared bytes in lockstep (module + client bindings).
- Version your payloads or include a schema tag when types may evolve.
When it happens
Trigger: Reading a scheduled reducer's stored argument rows after the module schema changed (old rows encode the old row type); decoding an HTTP request body as a type that does not match what the client serialized; module crate version differing between the writer and reader of the bytes.
Common situations: Redeploying a module that changed a scheduled reducer's argument type while scheduled rows persisted in the database; client and module disagreeing on the request body type after a partial upgrade.
Related errors
- a row was a sequence trigger but there was no generated colu
- unable to decode args
- Unrecognised extra bytes in the {description}
- Invalid tag value, this state should be unreachable.
- Tag {tag} is out of range of enum {typeof(T).Name}
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/8b8a380c7883e67d.
Report an issue: GitHub.