clockworklabs/SpacetimeDB · error · anyhow::Error

Could not find StdbModule.csproj at {}

Error message

Could not find StdbModule.csproj at {}

What it means

The NativeAOT configuration step (add_native_aot_packages_to_csproj) expects StdbModule.csproj at the project root and bails with the full expected path when it is missing (init.rs:1916). It is reached when building/publishing a C# module with native-aot enabled.

Source

Thrown at crates/cli/src/subcommands/init.rs:1916

    Ok(())
}

/// Adds NativeAOT-LLVM project configuration to an existing C# .csproj file and creates NuGet.Config.
///
/// The configuration differs depending on the target .NET version:
///
/// **.NET 8 AOT** (`--native-aot`): Keeps `net8.0` TFM and adds explicit ILCompiler.LLVM 8.0.0-*
/// package references, gated on `EXPERIMENTAL_WASM_AOT=1`.
///
/// **.NET 10 AOT**: Replaces the TFM with `net10.0` directly (no conditional needed since the
/// project is definitively targeting .NET 10). ILCompiler.LLVM refs are provided transitively
/// by the SpacetimeDB.Runtime NuGet package.
///
/// Both paths need a NuGet.Config with the dotnet-experimental feed for ILCompiler.LLVM resolution.
fn add_native_aot_packages_to_csproj(project_path: &Path, dotnet_major: Option<u8>) -> anyhow::Result<()> {
    let csproj_path = project_path.join("StdbModule.csproj");
    if !csproj_path.exists() {
        anyhow::bail!("Could not find StdbModule.csproj at {}", csproj_path.display());
    }

    let content = std::fs::read_to_string(&csproj_path)?;

    let new_content = if dotnet_major == Some(8) {
        // .NET 8 AOT: keep net8.0 TFM, add explicit ILCompiler.LLVM package references.
        let native_aot_config = r#"
  <ItemGroup Condition="'$(EXPERIMENTAL_WASM_AOT)' == '1'">
    <PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
    <PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
  </ItemGroup>
"#;
        if let Some(pos) = content.rfind("</Project>") {
            let (before, after) = content.split_at(pos);
            format!("{}{}{}", before.trim_end(), native_aot_config, after)
        } else {
            anyhow::bail!("Invalid .csproj file: missing </Project> tag");
        }

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Run from (or pass --project-path to) the directory that contains StdbModule.csproj
  2. Rename your csproj back to StdbModule.csproj
  3. Remove the native-aot setting/flag when building non-C# modules
  4. Regenerate the module with `spacetime init` to restore the expected layout

Example fix

// before (repo root, no csproj here)
spacetime publish --project-path . --native-aot
// after
spacetime publish --project-path modules/csharp --native-aot
Defensive patterns

Strategy: validation

Validate before calling

test -f "$PROJECT_DIR/StdbModule.csproj" || {
  echo "StdbModule.csproj not found in $PROJECT_DIR" >&2; exit 2
}
spacetime publish --project-path "$PROJECT_DIR" --native-aot

Type guard

fn is_csharp_module_root(dir: &std::path::Path) -> bool {
    dir.join("StdbModule.csproj").is_file()
}

Prevention

When it happens

Trigger: Publishing with --native-aot (or native-aot: true in spacetime.json) from a directory that is not the C# module root, or after StdbModule.csproj was renamed or deleted.

Common situations: Running `spacetime publish` from the repo root instead of the module directory; a spacetime.json with a stale/wrong project-path; renaming the generated csproj; leaving native-aot enabled for a Rust or TypeScript target.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/833ad33633103503. Report an issue: GitHub.