clockworklabs/SpacetimeDB · error · anyhow::Error
Invalid .csproj file: missing </Project> tag
Error message
Invalid .csproj file: missing </Project> tag
What it means
On the .NET 8 AOT path, init appends ILCompiler.LLVM package references by locating the last `</Project>` closing tag and splicing before it. A csproj without that tag (truncated, hand-edited into invalid XML, or a forked template that dropped it) cannot be edited, so init aborts.
Solutions
- Open the csproj and fix the XML so it ends with a proper `</Project>`; validate with `dotnet build`
- Regenerate the C# project and re-apply custom edits afterwards
- Forked templates: keep a well-formed csproj with the closing tag
- Consider `--dotnet-version 10` AOT, which only string-replaces the TFM and is more tolerant
Example fix
<!-- before: truncated csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<!-- after -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project> Defensive patterns
Strategy: validation
Validate before calling
# quick XML sanity check before init's AOT step grep -q '</Project>' spacetimedb/StdbModule.csproj && echo ok || echo 'csproj not closed'
Prevention
- Keep the generated csproj well-formed; run `dotnet build` after hand edits
- Prefer the .NET 10 AOT path when possible; it only swaps the TFM string
When it happens
Trigger: --native-aot with --dotnet-version 8 against a malformed or truncated csproj; forked/custom template emitting nonstandard XML; file corrupted mid-write.
Common situations: Hand-edited csproj with mismatched tags; template forks; editors or lint tools mangling the file.
Related errors
- Could not find StdbModule.csproj at
- Invalid C# template: missing net8.0/net10.0…
- --native-aot is only supported for C# projects (--lang…
- --dotnet-version is only supported for C# projects (--lang…
- Argument must be a ConnectionId
AI-assisted analysis of clockworklabs/SpacetimeDB@fdd647dfac (2026-08-30).
Data as JSON: /api/errors/0285ce6f4f472c72.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/subcommands/init.rs:1970
"runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM",
"8.0.0-*",
);
if has_net8_native_aot_refs {
content
} else {
// .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");
}
}
} else {
// .NET 10 AOT: directly set TFM to net10.0 (no conditional needed).
// ILCompiler.LLVM comes transitively via the SpacetimeDB.Runtime NuGet package.
content.replace(
"<TargetFramework>net8.0</TargetFramework>",
"<TargetFramework>net10.0</TargetFramework>",
)
};
std::fs::write(&csproj_path, new_content)?;
println!(
"{} Added NativeAOT-LLVM project configuration to {}",
"✓".green(),
csproj_path.display()
);
View on GitHub (pinned to fdd647dfac)