clockworklabs/SpacetimeDB · error · Error
Subscriptions must have at least one query
Error message
Subscriptions must have at least one query
What it means
SubscriptionBuilderImpl.subscribe normalizes its argument (a SQL string, an array, or a callback returning query/queries) into a query list and requires at least one entry. An empty array — or a callback that returns an empty array — throws before any subscription message is sent.
Solutions
- Guard before subscribing: if (queries.length === 0) skip or throw your own config error
- If broad subscription is the intent, use subscribeToAllTables() instead
- Default the dynamic list to at least one explicit query such as 'SELECT * FROM <core_table>'
Example fix
// before
sub.subscribe(enabledTables.map(t => `SELECT * FROM ${t}`)); // [] -> throws
// after
if (enabledTables.length > 0) {
sub.subscribe(enabledTables.map(t => `SELECT * FROM ${t}`));
} Defensive patterns
Strategy: validation
Validate before calling
if (queries.length === 0) {
// skip subscribing, or throw a precise config error
throw new Error('no queries configured for subscription');
}
sub.onApplied(onApplied).subscribe(queries); Prevention
- Guard dynamically-built query lists for emptiness before subscribing
- Use subscribeToAllTables() when the intent is broad coverage
- Log the query list length at subscription time in debug builds
When it happens
Trigger: subscriptionBuilder().onApplied(cb).subscribe([]); or a callback like tables => enabledTables.map(t => tables[t].all()) where enabledTables is empty.
Common situations: Building subscriptions dynamically from configuration that can legitimately be empty; a filter step that removes every table; a previously hardcoded single query becoming data-driven.
Related errors
- Subscriptions must be SQL strings or typed queries
- Could not determine return type during RLS resolution
- Estimated cardinality
- Failed to determine TableId for query
- `fromCounterV7` requires `randomBytes.length == 4`
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/f6db69c19e757ef3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/bindings-typescript/src/sdk/subscription_builder_impl.ts:120
) => RowTypedQuery<any, any> | RowTypedQuery<any, any>[]
): SubscriptionHandleImpl<RemoteModule>;
subscribe(
query_sql:
| string
| RowTypedQuery<any, any>
| Array<string | RowTypedQuery<any, any>>
| ((tables: any) => RowTypedQuery<any, any> | RowTypedQuery<any, any>[])
): SubscriptionHandleImpl<RemoteModule> {
let queries: Array<string | RowTypedQuery<any, any>>;
if (typeof query_sql === 'function') {
const tables = this.db.getFromBuilder<RemoteModule & UntypedSchemaDef>();
const result = query_sql(tables);
queries = Array.isArray(result) ? result : [result];
} else {
queries = Array.isArray(query_sql) ? query_sql : [query_sql];
}
if (queries.length === 0) {
throw new Error('Subscriptions must have at least one query');
}
const queryStrings = queries.map(q => {
if (typeof q === 'string') return q;
if (isRowTypedQuery(q)) return toSql(q);
throw new Error('Subscriptions must be SQL strings or typed queries');
});
return new SubscriptionHandleImpl(
this.db,
queryStrings,
this.#onApplied,
this.#onError
);
}
/**
* Subscribes to all rows from all tables.
*
* This method is intended as a convenienceView on GitHub (pinned to 6dee26c6ef)