clockworklabs/SpacetimeDB · error · anyhow::Error
The selected C# build path requires the .NET
Error message
The selected C# build path requires the .NET {desired_sdk_major} SDK, but the active SDK is .NET {active}.
Update {} to select the .NET {desired_sdk_major} SDK, or run `spacetime init --dotnet-version {desired_sdk_major}` to create a project configured for this SDK.
Expected global.json contents:
{} What it means
The C# build path was selected (JIT/AOT-LLVM paths require the .NET 8 SDK, the NativeAOT path requires .NET 10), but the SDK that `dotnet --version` actively resolves (when a global.json applies) has a different major. Rather than emit a wasm built by the wrong toolchain, the CLI aborts and prints the exact global.json contents it expects at <project>/global.json, including an init-command alternative.
Solutions
- Write the exact global.json the error prints into the project directory, e.g. {"sdk":{"version":"8.0.100","rollForward":"latestFeature"}} for .NET 8.
- Or reconfigure via `spacetime init --dotnet-version <8|10>` on a fresh project.
- Or align the csproj <TargetFramework> (and --native-aot usage) with the SDK the global.json selects.
Example fix
// before: project/global.json + csproj mismatch
{"sdk":{"version":"10.0.100","rollForward":"latestMinor"}}
// <TargetFramework>net8.0</TargetFramework>
// after: pin the SDK the build path needs
{"sdk":{"version":"8.0.100","rollForward":"latestFeature"}} Defensive patterns
Strategy: validation
Validate before calling
# Ensure the active SDK matches the target before building
WANT=8
global.json: {"sdk":{"version":"${WANT}.0.100","rollForward":"latestFeature"}}
dotnet --version | cut -d. -f1 | grep -qx "$WANT" || { echo "active SDK != $WANT" >&2; exit 1; }
spacetime build Try / catch
On this failure, copy the exact global.json contents printed in the error message into <project>/global.json and re-run the build once
Prevention
- Generate global.json with `dotnet new globaljson --roll-forward latestFeature` pinned to the TFM's major.
- Keep csproj TargetFramework and the global.json pin in the same PR when bumping versions.
- Use `spacetime init --dotnet-version` when scaffolding so the pin is created correctly from the start.
When it happens
Trigger: `spacetime build` on a C# project where the csproj/flags select the .NET 8 path but a project- or cwd-level global.json resolves SDK 10, or vice versa; also --dotnet-version 8 with an active SDK 10 global.json pin.
Common situations: Repo-level global.json pinning SDK 10 while the module targets net8.0; side-by-side SDK installs where the wrong one resolves; templates created before a version bump leaving a stale pin.
Related errors
- {error}
- Unsupported .NET SDK version
- Unsupported .NET SDK version
- dotnet not found in PATH. Please install .NET SDK 8.0 or…
- --dotnet-version is only supported for C# projects (--lang…
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/b92cea7902394afa.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/tasks/csharp.rs:261
.unwrap_or_else(|| "<unknown>".to_string());
anyhow::bail!(
"Unsupported .NET SDK version: {}. SpacetimeDB requires .NET SDK 8.0 or 10.0.\n\
If you have multiple versions installed, configure your project using \
https://learn.microsoft.com/en-us/dotnet/core/tools/global-json, \
or use --dotnet-version to specify the target version explicitly.",
selected_version
);
}
};
let desired_sdk_major = match build_path {
CsharpBuildPath::Net8Jit | CsharpBuildPath::Net8Aot => 8,
CsharpBuildPath::Net10Aot => 10,
};
let active_sdk_major = dotnet_version_str.as_deref().and_then(parse_major_version);
if active_sdk_major != Some(desired_sdk_major) {
let active = dotnet_version_str.as_deref().map(str::trim).unwrap_or("<unknown>");
anyhow::bail!(
"The selected C# build path requires the .NET {desired_sdk_major} SDK, but the active SDK is .NET {active}.\n\
Update {} to select the .NET {desired_sdk_major} SDK, or run `spacetime init --dotnet-version {desired_sdk_major}` to create a project configured for this SDK.\n\
Expected global.json contents:\n{}",
project_path.join("global.json").display(),
dotnet_global_json(desired_sdk_major)?
);
}
// Manage the EXPERIMENTAL_WASM_AOT environment variable for MSBuild.
// - Net8Aot / Net10Aot: must SET it — the ILCompiler.LLVM.targets import in
// SpacetimeDB.Runtime.targets is gated on this env var. Without it, the NativeAOT
// toolchain is not activated and dotnet produces managed DLLs instead of a .wasm.
// - Net8Jit: must UNSET it — prevents MSBuild from incorrectly enabling NativeAOT mode
// when the env var is set globally (e.g., in CI).
match &build_path {
CsharpBuildPath::Net8Aot | CsharpBuildPath::Net10Aot => {
// SAFETY: We are single-threaded at this point and no other code is reading
// this environment variable concurrently.View on GitHub (pinned to 6dee26c6ef)