clockworklabs/SpacetimeDB · error
unable to encode view return value
Error message
unable to encode view return value
What it means
After a view executes, invoke_view serializes the return value to BSATN via retn.to_writer(&mut buf) and unwraps with .expect("unable to encode view return value") (crates/bindings/src/rt.rs:98). The sink is an in-memory IterBuf, so I/O failures are essentially impossible; a failure here would mean the VIEW RETURN TYPE cannot be serialized to BSATN at all — a type that declares SpacetimeType but hits an encoding edge (or interior misuse of a non-serializable value). It is a defensive invariant rather than an expected runtime error.
Solutions
- Confirm the view's return type is a supported SpacetimeType composition (rows, Vec, Option, primitives, table handle)
- Reduce the view to return Vec<RowType> — the canonical supported shape — and re-test
- If reproducible, file the minimal type definition upstream: this expect firing on a declared SpacetimeType is a bug in the bindings/encoder contract
Example fix
// before
fn custom(ctx: &ViewContext, ()) -> ExoticCustomType { /* panics at encode */ }
// after
fn custom(ctx: &ViewContext, ()) -> Vec<UserRow> {
ctx.db.user().iter().collect() // canonical, always encodable
} Defensive patterns
Strategy: validation
Validate before calling
// Keep view returns in the canonical always-encodable shapes: // Vec<RowT>, Option<RowT>, RowT where RowT: SpacetimeType (table row type). // There is no cheap runtime pre-check beyond using supported types.
Try / catch
// Panic-based invariant; no caller-side catch exists inside the module.
// Host may catch_unwind around invoke_view and report the view name.
let res = std::panic::catch_unwind(AssertUnwindSafe(|| invoke_view(...)));
res.map_err(|_| anyhow::anyhow!("view return type failed BSATN encode; unsupported return shape"))?; Prevention
- Restrict view return types to generated row types and standard containers
- Avoid bespoke custom SpacetimeType impls on view returns unless well-tested
- If you can reproduce this, minimize the type and report it as a bindings bug
When it happens
Trigger: A view returning a type whose BSATN serialization is fallible and actually errors (exotic custom/algebraic types, or values violating invariants the encoder assumes); memory allocation failure mid-encode surfacing as an error. Ordinary Vec<T>/Option<T>/row returns over well-formed SpacetimeTypes never take this path.
Common situations: Practically unreachable for standard types; appears only in tests that construct degenerate return types or if a new type integration in the module crate is unsound. Most developers will never see it.
Related errors
- unable to decode args
- heterogeneous array
- a row was a sequence trigger but there was no generated…
- AddView: view ` ` not found in new module def
- Cannot serialize a null string as BSATN String. To…
AI-assisted analysis of clockworklabs/SpacetimeDB@eddf9f5014 (2026-08-20).
Data as JSON: /api/errors/9a0b9c19a34eb302.
Report an issue: GitHub.
Appendix: source
Thrown at crates/bindings/src/rt.rs:98
note = "where each `Ti` type implements `SpacetimeType`.",
note = ""
)]
pub trait Reducer<'de, A: Args<'de>> {
fn invoke(&self, ctx: &ReducerContext, args: A) -> ReducerResult;
}
/// Invoke a caller-specific view.
/// Returns a BSATN encoded `Vec` of rows.
pub fn invoke_view<'a, A: Args<'a>, T: ViewReturn>(
view: impl View<'a, A, T>,
ctx: ViewContext,
args: &'a [u8],
) -> Vec<u8> {
// Deserialize the arguments from a bsatn encoding.
let SerDeArgs(args) = bsatn::from_slice(args).expect("unable to decode args");
let retn = view.invoke(&ctx, args);
let mut buf = IterBuf::take();
retn.to_writer(&mut buf).expect("unable to encode view return value");
std::mem::take(&mut *buf)
}
/// A trait for types representing the execution logic of a caller-specific view.
#[expect(clippy::duplicated_attributes, reason = "false positive")]
#[diagnostic::on_unimplemented(
message = "invalid view signature",
label = "this view signature is not valid",
note = "",
note = "view signatures must match:",
note = " `Fn(&ViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`",
note = "where each `Ti` implements `SpacetimeType`.",
note = ""
)]
pub trait View<'de, A: Args<'de>, T: ViewReturn> {
fn invoke(&self, ctx: &ViewContext, args: A) -> T;
}
/// Invoke an anonymous view.View on GitHub (pinned to eddf9f5014)