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

  1. Confirm the view's return type is a supported SpacetimeType composition (rows, Vec, Option, primitives, table handle)
  2. Reduce the view to return Vec<RowType> — the canonical supported shape — and re-test
  3. 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

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


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)