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 the React component tree under a `SpacetimeDBProvider` component.

What it means

useTable calls useSpacetimeDB inside a try/catch; when no provider is mounted the context hook throws, and useTable re-throws this 'Could not find SpacetimeDB client' message. The root cause is exactly the missing-provider case: no SpacetimeDBProvider in the component tree above the hook call.

Solutions

  1. Mount SpacetimeDBProvider above every component that uses useTable
  2. Use a shared test wrapper component that includes the provider
  3. Verify the provider is not conditionally skipped on the render that first runs the hook

Example fix

// before
export function Players() {
  const [rows] = useTable(playersQuery); // throws: no provider above
  ...
}

// after
root.render(
  <SpacetimeDBProvider connectionBuilder={builder}>
    <Players />
  </SpacetimeDBProvider>
);
Defensive patterns

Strategy: try-catch

Try / catch

// Error boundary (same shape as for useSpacetimeDB) around components using useTable
class TableErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { error?: Error }
> {
  state = { error: undefined as Error | undefined };
  static getDerivedStateFromError(error: Error) { return { error }; }
  render() {
    if (this.state.error?.message.includes('SpacetimeDBProvider')) {
      return <p>Connect this view to SpacetimeDB first.</p>;
    }
    return this.props.children;
  }
}

Prevention

When it happens

Trigger: Using useTable(query, callbacks) in a component rendered outside <SpacetimeDBProvider connectionBuilder={...}>.

Common situations: Same as other missing-provider cases: unwrapped test renders, a new page or modal outside the provider subtree, provider skipped during the first render pass.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/03841878d0e42763. Report an issue: GitHub.

Appendix: source

Thrown at crates/bindings-typescript/src/react/useTable.ts:81

 *   { onInsert: (row) => console.log('New user:', row) }
 * );
 * ```
 */
export function useTable<TableDef extends UntypedTableDef>(
  query: Query<TableDef>,
  callbacks?: UseTableCallbacks<Prettify<RowType<TableDef>>>
): [readonly Prettify<RowType<TableDef>>[], boolean] {
  type UseTableRowType = RowType<TableDef>;
  const enabled = callbacks?.enabled ?? true;
  const accessorName = getQueryAccessorName(query);
  const whereExpr = getQueryWhereClause(query);

  const [subscribeApplied, setSubscribeApplied] = useState(false);
  let connectionState: ConnectionState | undefined;
  try {
    connectionState = useSpacetimeDB();
  } catch {
    throw new Error(
      'Could not find SpacetimeDB client! Did you forget to add a ' +
        '`SpacetimeDBProvider`? `useTable` must be used in the React component tree ' +
        'under a `SpacetimeDBProvider` component.'
    );
  }

  const querySql = toSql(query);

  const latestTransactionEventId = useRef<string | null>(null);
  const lastSnapshotRef = useRef<
    [readonly Prettify<UseTableRowType>[], boolean] | null
  >(null);

  const computeSnapshot = useCallback((): [
    readonly Prettify<UseTableRowType>[],
    boolean,
  ] => {
    if (!enabled) {

View on GitHub (pinned to 6dee26c6ef)