clockworklabs/SpacetimeDB · error
SQL query exceeds maximum allowed length: \"{sql:.120}...\"
Error message
SQL query exceeds maximum allowed length: \"{sql:.120}...\" What it means
compile_subscription rejects any SQL text longer than 50,000 UTF-8 bytes (MAX_SQL_LENGTH). The cap is a deliberate guard, flagged as a 'DIRTY HACK' in source, against stack overflow when the compiler recurses over queries with deeply nested AND/OR condition trees.
Source
Thrown at crates/query/src/lib.rs:32
use spacetimedb_physical_plan::{
compile::{compile_dml_plan, compile_select, compile_select_list},
plan::{ProjectListPlan, ProjectPlan},
};
use spacetimedb_primitives::TableId;
use spacetimedb_schema::table_name::TableName;
/// DIRTY HACK ALERT: Maximum allowed length, in UTF-8 bytes, of SQL queries.
/// Any query longer than this will be rejected.
/// This prevents a stack overflow when compiling queries with deeply-nested `AND` and `OR` conditions.
const MAX_SQL_LENGTH: usize = 50_000;
pub fn compile_subscription(
sql: &str,
tx: &impl SchemaView,
auth: &AuthCtx,
) -> Result<(Vec<ProjectPlan>, TableId, TableName, bool)> {
if sql.len() > MAX_SQL_LENGTH {
bail!("SQL query exceeds maximum allowed length: \"{sql:.120}...\"")
}
let (plan, mut has_param) = parse_and_type_sub(sql, tx, auth)?;
let Some(return_id) = plan.return_table_id() else {
bail!("Failed to determine TableId for query")
};
let Some(return_name) = tx.schema_for_table(return_id).map(|schema| schema.table_name.clone()) else {
bail!("TableId `{return_id}` does not exist")
};
// Resolve any RLS filters
let plan_fragments = resolve_views_for_sub(tx, plan, auth, &mut has_param)?
.into_iter()
.map(compile_select)
.collect::<Vec<_>>();
View on GitHub (pinned to 6dee26c6ef)
Solutions
- Replace long OR/IN chains with a join against a table containing the keys, or filter on a scalar column.
- Split the subscription into several smaller subscriptions and merge results client-side.
- Persist the filter keys via a reducer into a helper table and subscribe with a join against it.
Example fix
-- before: thousands of OR'd predicates, > 50KB SELECT * FROM t WHERE id = 1 OR id = 2 OR id = 3 /* ... */; -- after: keys live in a table, query stays tiny SELECT t.* FROM t JOIN selected_ids s ON t.id = s.id;
Defensive patterns
Strategy: validation
Validate before calling
// client-side guard before subscribing (limit is 50_000 UTF-8 bytes)
const MAX_SQL_LENGTH = 50_000;
if (new TextEncoder().encode(sql).length > MAX_SQL_LENGTH) {
throw new Error(`SQL too large (${sql.length} bytes); split or use a helper-table join`);
} Try / catch
try {
await db.subscription.build([sql]).subscribe();
} catch (e: any) {
if (String(e.message).includes("exceeds maximum allowed length")) {
// split into multiple smaller subscriptions, or join a keys table
}
} Prevention
- Never build subscriptions by string-concatenating per-id predicates; join a keys table instead.
- Keep subscription SQL hand-written and short; generate data, not queries.
- Add a byte-length assert on SQL strings in test suites that generate queries.
When it happens
Trigger: Calling subscribe with a SQL string over 50,000 bytes, typically machine-generated WHERE clauses with hundreds or thousands of OR'd predicates (big IN-list expansions).
Common situations: Dynamically building a subscription from a large entity/id list; clients concatenating per-key predicates into one query; generated SQL from ORMs or scripts pasted into clients.
Related errors
- Invalid number of tables in subscription: {}
- Event tables cannot be used as the lookup table in subscript
- Index '${indexLabel}' on table '${tableLabel}' must define a
- Invalid hex UUID
- Subscriptions must have at least one query
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/018ca3aba635363b.
Report an issue: GitHub.