clockworklabs/SpacetimeDB · error

Cannot determine module type from file extension

Error message

Cannot determine module type from file extension

What it means

schema_extract::from_path infers the module host type from the file extension: .wasm maps to Wasm and .js to Js. Any other (or missing) extension makes host-type inference impossible, so the function bails with this anyhow error instead of guessing.

Solutions

  1. Rename/copy the module file so it ends in .wasm or .js.
  2. Verify you are passing the compiled module path, not a source file (.rs, .ts) or intermediate artifact.
  3. Check the path for typos and that the file actually exists with the expected extension.

Example fix

// before
spacetime schema extract ./target/server_component
// after
cp ./target/server_component ./target/server_component.wasm
spacetime schema extract ./target/server_component.wasm
Defensive patterns

Strategy: validation

Validate before calling

const ok = /\.(wasm|js)$/.test(modulePath); if (!ok) throw new Error(`Module path must end in .wasm or .js: ${modulePath}`);

Try / catch

// CLI: anyhow bail is surfaced; wrap invocation and print extension guidance

Prevention

When it happens

Trigger: Running `spacetime schema extract` (or generate client bindings offline) on a program file whose extension is not exactly .wasm or .js — e.g. .wat, .wasm.tmp, no extension, or a copied binary without its suffix.

Common situations: Pointing the CLI at a build artifact with a hash/temp suffix, a symlink without extension, or a module written in a language outputting another container format.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@eddf9f5014 (2026-09-20). Data as JSON: /api/errors/818e09803c977ce9. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/schema_extract.rs:29

};
use tokio::io::AsyncReadExt;

fn extractor_path() -> anyhow::Result<PathBuf> {
    std::env::var_os("SPACETIMEDB_SCHEMA_EXTRACTOR")
        .map(PathBuf::from)
        .map(Ok)
        .unwrap_or_else(|| crate::util::resolve_sibling_binary("spacetimedb-standalone"))
}

/// Keep generate's synchronous injectable function API. Its small dedicated
/// runtime works both inside and outside a caller's Tokio runtime; process
/// ownership and validation are identical to publish's async path.
pub(crate) fn from_path(path: &Path) -> anyhow::Result<ModuleDef> {
    let bytes = read_program(path)?;
    let host_type = match path.extension().and_then(|ext| ext.to_str()) {
        Some("wasm") => "Wasm",
        Some("js") => "Js",
        _ => anyhow::bail!("Cannot determine module type from file extension"),
    };
    inspect_blocking(extractor_path()?, bytes, host_type.into())
}

fn inspect_blocking(extractor: PathBuf, bytes: Vec<u8>, host_type: String) -> anyhow::Result<ModuleDef> {
    std::thread::spawn(move || {
        tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()?
            .block_on(inspect_with(extractor, bytes, host_type, INSPECT_TIMEOUT))
    })
    .join()
    .map_err(|_| anyhow::anyhow!("Local module inspection thread failed"))?
}

pub(crate) fn read_program(path: &std::path::Path) -> anyhow::Result<Vec<u8>> {
    use std::io::Read;
    let mut bytes = Vec::new();

View on GitHub (pinned to eddf9f5014)