clockworklabs/SpacetimeDB · error · anyhow::Error

For some reason, your project has both a `bin` and a `bin~`…

Error message

For some reason, your project has both a `bin` and a `bin~` folder.
I don't know which to use, so please delete both and rerun this command so that we can see which is up-to-date.

What it means

Expected C# output locations come in bin/ and bin~ pairs (bin~ is a rename/backup some tooling produces). If both variants of the same output path exist after a successful publish, the CLI cannot tell which is current and aborts instead of guessing which StdbModule.wasm to upload.

Solutions

  1. Delete both bin and bin~ (and obj, obj~ while at it), then re-run the build/publish.
  2. If it recurs, find what keeps creating bin~ (sync/backup tool) and exclude the project directory from it.

Example fix

# before
spacetime build
# after
rm -rf bin bin~ obj obj~
spacetime build
Defensive patterns

Strategy: validation

Validate before calling

# Pre-build hygiene: never enter a build with split bin folders
[ ! -d bin ] || [ ! -d bin~ ] || { echo "both bin and bin~ present; cleaning" >&2; rm -rf bin bin~; }
spacetime build

Prevention

When it happens

Trigger: `spacetime build` succeeds but both e.g. bin/Release/net8.0/wasi-wasm/.../StdbModule.wasm and its bin~ twin exist -- typically after an interrupted build, or a watcher/backup tool renaming bin to bin~ between builds.

Common situations: Interrupted or concurrent builds; editors/antivirus/sync tools renaming directories; repeatedly switching between JIT and AOT build modes in the same tree.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@9e0d92412f (2026-08-20). Data as JSON: /api/errors/709491eef9459a63. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/tasks/csharp.rs:463

        )),
        // NativeAOT-LLVM outputs to 'native' subdirectory instead of 'publish'
        project_path.join(format!(
            "bin/{config_name}/{target_framework}/wasi-wasm/{subdir}/StdbModule.wasm"
        )),
        project_path.join(format!(
            "bin~/{config_name}/{target_framework}/wasi-wasm/{subdir}/StdbModule.wasm"
        )),
        // Also check for raw wasm output without wasi-wasm RID folder (NativeAOT-LLVM sometimes does this)
        project_path.join(format!("bin/{config_name}/{target_framework}/native/StdbModule.wasm")),
        project_path.join(format!("bin~/{config_name}/{target_framework}/native/StdbModule.wasm")),
    ];
    // Check if both bin and bin~ variants exist for the same output path (indicates a conflict)
    for i in (0..possible_output_paths.len()).step_by(2) {
        if i + 1 < possible_output_paths.len() {
            let bin_path = &possible_output_paths[i];
            let bin_tilde_path = &possible_output_paths[i + 1];
            if bin_path.exists() && bin_tilde_path.exists() {
                anyhow::bail!(concat!(
                    "For some reason, your project has both a `bin` and a `bin~` folder.\n",
                    "I don't know which to use, so please delete both and rerun this command so that we can see which is up-to-date."
                ));
            }
        }
    }
    for output_path in &possible_output_paths {
        if output_path.exists() {
            return Ok(output_path.clone());
        }
    }

    // Diagnostic: list what we expected and what actually exists in the output directories
    eprintln!("Build path: {build_path:?}, target_framework: {target_framework}, subdir: {subdir}");
    eprintln!("Expected output in one of:");
    for p in &possible_output_paths {
        eprintln!("  {}", p.display());
    }

View on GitHub (pinned to 9e0d92412f)