clockworklabs/SpacetimeDB · error · anyhow::Error
query returns `{}` but view expects `{}`
Error message
query returns `{}` but view expects `{}` What it means
For each plan, the host compares the row type of the source table the query returns against the view's expected row type. If column count, order, or types differ, view creation is rejected and both row types are printed so the exact mismatch is visible. The comparison is on the full product type, not just column names.
Source
Thrown at crates/core/src/host/wasm_common/module_host_actor.rs:200
let schema_view = SchemaViewer::new(&*tx, &auth);
// Compile to subscription plans.
let (plans, has_params) = SubscriptionPlan::compile(the_query, &schema_view, &auth)?;
ensure!(
!has_params,
"parameterized SQL is not supported for view materialization yet"
);
// Validate shape and disallow views-on-views.
for plan in &plans {
let Some(source_schema) = plan.return_table() else {
bail!("query does not return plain table rows");
};
if plan.reads_from_view(true) || plan.reads_from_view(false) {
bail!("view definition cannot read from other views");
}
if source_schema.row_type != *expected_row_type {
bail!(
"query returns `{}` but view expects `{}`",
fmt_algebraic_type(&AlgebraicType::Product(source_schema.row_type.clone())),
fmt_algebraic_type(&AlgebraicType::Product(expected_row_type.clone())),
);
}
}
let op = FuncCallType::View(call_info.clone());
let mut metrics = ExecutionMetrics::default();
let mut rows = Vec::new();
let params = ExecutionParams::from_auth(&auth);
for plan in plans {
// Track read sets for all tables involved in this plan.
// TODO(jsdt): This means we will rerun the view and query for any change to these tables, so we should optimize this asap.
for table_id in plan.table_ids() {
tx.record_table_scan(&op, table_id);View on GitHub (pinned to 9e0d92412f)
Solutions
- If the view should mirror the whole row, use SELECT * FROM t
- Otherwise list exactly the columns, in order and with matching types, of the expected row type shown in the error message
- After altering a table, recreate dependent views to match the new schema
- Diff the two row types printed in the message to find the offending column
Example fix
-- before: subset drifts from the expected row (id, name, email) CREATE VIEW v AS SELECT name, email FROM users; -- after: match the row type exactly CREATE VIEW v AS SELECT id, name, email FROM users;
Defensive patterns
Strategy: validation
Validate before calling
-- confirm the source row shape before writing the view spacetime sql my-db 'SELECT * FROM users LIMIT 0'; -- shows exact columns/order -- then mirror exactly those columns (and order) in CREATE VIEW
Prevention
- Default to SELECT * when the view should mirror the row
- After any table alter, recreate dependent views in the same change
- Use the two row types printed by the error to pinpoint the divergent column
- Avoid expressions/casts in view column lists - they change the row type
When it happens
Trigger: SELECTing a subset of columns or a reordered list; replacing a column with an expression or cast; the view declaration expecting a row type that no longer matches the source table after the table's schema changed.
Common situations: Hand-written column lists drifting from the table definition; a table altered (column added/retyped) while dependent views stayed unchanged; reordering columns for convenience in the view.
Related errors
- Index '${indexLabel}' on table '${tableLabel}' must define a
- query does not return plain table rows
- view definition cannot read from other views
- Cannot define RLS rule on private table: {}. Please make tab
- Literal values for type {} are not supported
AI-assisted analysis of clockworklabs/SpacetimeDB@9e0d92412f (2026-08-20).
Data as JSON: /api/errors/df7a4fa790751a0d.
Report an issue: GitHub.