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
- Find real version names with spacetime update list (installed view) and uninstall one of those, e.g. spacetime update uninstall 1.6.0.
- 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
- Populate uninstall scripts from `spacetime update list` output, never from raw directory listings.
- Treat 'current' as a symlink name, not a version, in all automation.
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
- Cannot extract table name from query
- Tried to read byte(s) at relative offset , but only byte(s)…
- v is not installed
- Cannot extract accessor name from query
- could not serialize result: object had neither a `ok` nor…
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)