clockworklabs/SpacetimeDB · error · Error

Cannot semijoin a table to itself

Error message

Cannot semijoin a table to itself

What it means

spacetime update uninstall current is rejected up front: current is the name of the symlink inside the CLI bin dir (BinDir::CURRENT_VERSION_DIR_NAME), not a real version. Removing 'that version' is ambiguous and would corrupt the install, so the command guards against the literal name.

Solutions

  1. Find real version names with spacetime update list (installed view) and uninstall one of those, e.g. spacetime update uninstall 1.6.0.
  2. If the goal is a full cleanup, uninstall every listed version, then remove the leftover current symlink manually.

Example fix

# before
spacetime update uninstall current

# after
spacetime update list            # shows installed versions
spacetime update uninstall 1.6.0
Defensive patterns

Strategy: validation

Validate before calling

[[ "$VERSION" != "current" ]] \
  || { echo "uninstall a concrete version, not 'current'"; exit 1; }

Prevention

When it happens

Trigger: Passing the literal string current as the version argument: spacetime update uninstall current.

Common situations: Users see a current entry next to the version directories and assume it is a version; scripts iterate directory entries and feed each name to uninstall.

Related errors


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

Appendix: source

Thrown at crates/bindings-typescript/src/lib/query.ts:117

      predicate: (row: RowExpr<TableDef>) => PredicateExpr<TableDef>
    ): SemijoinBuilder<TableDef>;
    /** @deprecated No longer needed — builder is already a valid query. */
    build(): Query<TableDef>;
  }>;

class SemijoinImpl<TableDef extends TypedTableDef>
  implements SemijoinBuilder<TableDef>, TableTypedQuery<TableDef>
{
  readonly [QueryBrand] = true;
  readonly type = 'semijoin' as const;
  constructor(
    readonly sourceQuery: FromBuilder<TableDef>,
    readonly filterQuery: FromBuilder<any>,
    readonly joinCondition: BooleanExpr<any>
  ) {
    if (sourceQuery.table.sourceName === filterQuery.table.sourceName) {
      // TODO: Handle aliasing properly instead of just forbidding it.
      throw new Error('Cannot semijoin a table to itself');
    }
  }

  build(): Query<TableDef> {
    return this as Query<TableDef>;
  }

  where(
    predicate: (row: RowExpr<TableDef>) => PredicateExpr<TableDef>
  ): SemijoinImpl<TableDef> {
    const nextSourceQuery = this.sourceQuery.where(predicate);
    return new SemijoinImpl<TableDef>(
      nextSourceQuery,
      this.filterQuery,
      this.joinCondition
    );
  }

View on GitHub (pinned to 6dee26c6ef)