clockworklabs/SpacetimeDB · error · TypeError
exporting something that is not a spacetime export
Error message
exporting something that is not a spacetime export
What it means
During `buildRawModuleDefV10`, `registerModuleExports` iterates the module's exports and requires every non-`default` export to be a spacetime export — an object/function carrying the hidden `registerExport` marker (`isModuleExport`). Plain functions, constants, classes, or third-party values exported from the module entry throw 'exporting something that is not a spacetime export'. The `default` export (the schema/router) is explicitly skipped.
Solutions
- Remove non-spacetime exports from the module entry file: keep helpers module-local (no `export`) or move them to a separate non-entry file.
- Keep only spacetime exports (reducers, procedures, views, tables via the schema, HTTP handlers/router) and the `default` schema/router export in the entry.
- If you need to expose constants, put them in a sibling file that is not the module entry compiled for publish.
Example fix
// before (module entry)
export const TICK_RATE = 5; // plain constant
export const tick = spacetimedb.reducer(...);
export default schema({ ... });
// after (module entry)
const TICK_RATE = 5; // module-local
export const tick = spacetimedb.reducer(...);
export default schema({ ... }); Defensive patterns
Strategy: type-guard
Validate before calling
// Before publish, scan entry exports and fail with your own message listing offenders.
function assertOnlySpacetimeExports(exports: Record<string, unknown>) {
const bad = Object.entries(exports)
.filter(([name, exp]) => name !== 'default' && !spacetimeExportNames.has(exp));
if (bad.length > 0) throw new Error(`non-spacetime exports: ${bad.map(b => b[0]).join(', ')}`);
}
// spacetimeExportNames = a Set you populate whenever you create a reducer/view/handler Type guard
// Structural guard mirroring isModuleExport (needs access to the same symbol module):
function isModuleExport(x: unknown): boolean {
return (typeof x === 'function' || typeof x === 'object') && x !== null && 'registerExport' in (x as object);
} Prevention
- Keep the module entry minimal: spacetime exports plus `export default schema/router`.
- Put helper functions, constants, and types in separate non-entry files.
- Add a lint rule or script that flags unknown export names in the entry file.
When it happens
Trigger: `export const VERSION = '1.0';`, `export function helper() {}`, `export class Foo {}`, or re-exporting a non-spacetime symbol from the module entry; exporting a type-assertion helper next to reducers. Submodule dispatch skips non-exports internally via `ignoreNonModuleExports: true`, but the top-level registration does not.
Common situations: Growing a module entry with utility exports (`export { connect }` helpers for tests); barrel files re-exporting test fixtures; library modules whose public API mixes spacetime exports with ordinary constants.
Related errors
- HTTP handler ' ' was exported more than once
- HTTP route for path ' ' refers to a handler that was not…
- invalid sequence type
- multiple schemas are not supported
- Route conflict for
AI-assisted analysis of clockworklabs/SpacetimeDB@eddf9f5014 (2026-08-20).
Data as JSON: /api/errors/cc2b02a43c661484.
Report an issue: GitHub.
Appendix: source
Thrown at crates/bindings-typescript/src/server/schema.ts:829
}
function registerModuleExports(
schema: SchemaInner,
exports: object,
opts?: { ignoreNonModuleExports?: boolean }
) {
if (schema.exportsRegistered) {
return;
}
schema.exportsRegistered = true;
for (const [name, moduleExport] of Object.entries(exports)) {
if (name === 'default') continue;
if (!isModuleExport(moduleExport)) {
if (opts?.ignoreNonModuleExports) {
continue;
}
throw new TypeError('exporting something that is not a spacetime export');
}
checkExportContext(moduleExport, schema);
moduleExport[registerExport](schema, name);
}
}
export function schema<
const H extends Record<string, SchemaEntry>,
const E extends EnvironmentSchema = {},
>(
entries: H,
moduleSettings?: ModuleSettings<E>
): Schema<SchemaDefForEntries<H> & { env: E }> {
const ctx = new SchemaInner<SchemaDefForEntries<H> & { env: E }>(ctx => {
ctx.moduleDef.environment = environmentDeclarations(
moduleSettings?.env ?? {}
);
// Apply module settings.View on GitHub (pinned to eddf9f5014)