clockworklabs/SpacetimeDB · error · Error
Could not find SpacetimeDB client! Did you forget to add a…
Error message
Could not find SpacetimeDB client! Did you forget to add a `SpacetimeDBProvider`? `useTable` must be used in a Vue component tree under a `SpacetimeDBProvider` component.
What it means
useTable in the Vue bindings wraps useSpacetimeDB() in a try/catch and rethrows this message when inject(SPACETIMEDB_INJECTION_KEY) returned undefined. Identical root cause as the base hook — no SpacetimeDBProvider above the component, or a call outside setup() — surfaced here with useTable-specific guidance.
Solutions
- Place <SpacetimeDBProvider :connectionBuilder="builder"> at the app root, above all components that read tables
- Keep the useTable call inside setup() (directly or through a composable called from setup)
- Verify both provider and hook are imported from the Vue bindings
- In unit tests, wrap the mounted component with the provider
Example fix
// before
export default defineComponent({
setup() {
const [rows, isReady] = useTable(tables.player); // throws
return { rows, isReady };
},
});
// after: App.vue
// <template>
// <SpacetimeDBProvider :connectionBuilder="builder"><PlayerList /></SpacetimeDBProvider>
// </template> Defensive patterns
Strategy: try-catch
Validate before calling
import { inject } from 'vue';
import { SPACETIMEDB_INJECTION_KEY } from './connection_state';
// inside setup():
if (inject(SPACETIMEDB_INJECTION_KEY, null) === null) {
// skip table wiring; provider is missing
} Try / catch
// parent-level catch for components that use useTable:
// <Suspense>
// <component :errorCaptured="(e) => { log(e); return false; }">
// or define an errorCaptured hook in the owning component Prevention
- Mount the provider at the app root, not per-route
- Keep table components under the provider in the tree; add a mount-order test
- Wrap table components with errorCaptured boundaries during development
When it happens
Trigger: Calling useTable(tables.x) from the Vue bindings in a component without a <SpacetimeDBProvider :connectionBuilder="..."> ancestor; invoking the composable outside setup() context; using the provider from a different framework binding set.
Common situations: Multi-page apps where the provider is only around <RouterView> children but a header/footer component also reads tables; tests mounting a table component bare; refactors that moved the provider into a child route component.
Related errors
- useSpacetimeDB must be used within a SpacetimeDBProvider…
- Could not find SpacetimeDB client! Did you forget to call…
- Could not find SpacetimeDB client! Did you forget to add a…
- useSpacetimeDB must be used within a component that called…
- useSpacetimeDB must be used within a SpacetimeDBProvider…
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/9f202fd4dceffe4f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/bindings-typescript/src/vue/useTable.ts:74
* @returns A tuple of [rows, isReady].
*/
export function useTable<TableDef extends UntypedTableDef>(
query: Query<TableDef>,
callbacks?: UseTableCallbacks<Prettify<RowType<TableDef>>>
): [
DeepReadonly<Ref<readonly Prettify<RowType<TableDef>>[]>>,
DeepReadonly<Ref<boolean>>,
] {
type Row = RowType<TableDef>;
const accessorName = getQueryAccessorName(query);
const whereExpr = getQueryWhereClause(query);
const querySql = toSql(query);
let conn;
try {
conn = useSpacetimeDB();
} catch {
throw new Error(
'Could not find SpacetimeDB client! Did you forget to add a ' +
'`SpacetimeDBProvider`? `useTable` must be used in a Vue component tree ' +
'under a `SpacetimeDBProvider` component.'
);
}
const rows = shallowRef<readonly Prettify<Row>[]>([]);
const isReady = ref(false);
let latestTransactionEvent: any = null;
let unsubscribeFromTable: (() => void) | null = null;
let subscriptionHandle: { unsubscribe: () => void } | null = null;
const computeFilteredRows = (): readonly Prettify<Row>[] => {
const connection = conn.getConnection();
if (!connection) return [];
const table = connection.db[accessorName];View on GitHub (pinned to 6dee26c6ef)