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

  1. Place <SpacetimeDBProvider :connectionBuilder="builder"> at the app root, above all components that read tables
  2. Keep the useTable call inside setup() (directly or through a composable called from setup)
  3. Verify both provider and hook are imported from the Vue bindings
  4. 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

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


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)