Diagnosing mismatches between IDE diagnostics and the real build: design-time builds, SDK resolution and global.json, stale caches, restore problems, source generators and multi-targeting.
The IDE's errors come from design-time builds and the language service, not from the real compiler run, so a mismatch means the IDE has a different view of the project. I first confirm which side reports the error by filtering the Error List to Build only and IntelliSense only. Then I check the usual causes: the IDE resolved a different SDK from the CLI (fix with global.json), stale caches in obj/ or .vs/, a failed restore, a source generator that has not re-run, or the wrong target framework context in a multi-targeted project. I fix the root cause and confirm with a clean dotnet build.
"IntelliSense errors come from design-time builds and the language service, not from the real compiler run, so a mismatch means the IDE has a different view of the project. I first prove which side is wrong by filtering the Error List to Build only and IntelliSense only; a restart is fine once for a glitch, but the mistake I see is treating it as the fix when the errors keep coming back. Then I work through the causes in order: the IDE resolving a different SDK from
dotnet --versionandglobal.json, a failed restore with noobj/project.assets.json, staleobj/and.vs/caches, conditional references or the wrong solution loaded, and finally source generators or the wrong target framework context in a multi-targeted project.For example, after we moved [our order-processing service] to .NET 10, CI was green but [about half the team] had IntelliSense errors on the new C# 14 code. I had to find out why the IDE and CI disagreed and stop it happening again. I used the Error List filter to show the errors were IntelliSense-only, compared
dotnet --versionwith each developer's IDE version, and found they were still on Visual Studio 2022, which doesn't support .NET 10. I moved everyone to Visual Studio 2026 or a current Rider, addedglobal.jsonto pin the SDK, and documented minimum IDE versions. The phantom errors were gone [within a day], and we had no repeat on the next upgrade.When anything is still unclear, I verify with a clean clone and
dotnet build -bl, because the binary log shows exactly which SDK, properties and targets MSBuild used. A recurring IDE-versus-CLI mismatch is a repository defect, so the fix belongs in the repo, not in everyone deleting their .vs folders."
A restart of the IDE or language server can clear a transient glitch, such as a language server that crashed during a branch switch or a large merge. That is a reasonable first move. If the errors come back, or several people see them, there is a real cause in the environment or the repo and the steps below find it.
Run dotnet --version in the repo folder; it honours global.json. If the IDE is older than the SDK the repo needs (for example Visual Studio 2022 against a net10.0 project that uses C# 14), the language service will not understand new syntax even though the CLI build works. Pin the SDK in global.json and make sure everyone runs an IDE that supports it.
PackageReference or ProjectReference with a Condition (for example on $(Configuration) or an environment variable) can be present in the CLI build and missing in the IDE's evaluation, or the other way round.HintPath to a DLL outside the repo work on one machine only.dotnet.defaultSolution decides which solution C# Dev Kit loads when a folder has several.obj/ folders (and bin/ if needed)..vs/ folder. Rider: File → Invalidate Caches. VS Code: run .NET: Restart Language Server or Developer: Reload Window.dotnet build-server shutdown to stop compiler and MSBuild server processes holding old state.IntelliSense needs obj/project.assets.json. If a private NuGet feed needs credentials the IDE does not have, the CLI may restore fine while the IDE fails. Run dotnet restore and check the IDE's restore output.
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>, the editor shows errors for the selected project context (the dropdown at the top-left of the Visual Studio editor). An API that only exists in net10.0 shows as an error in the net8.0 context. That is correct behaviour and needs #if NET10_0_OR_GREATER or a different API.Build from a clean clone with dotnet build -bl and inspect the binary log in the MSBuild Structured Log Viewer if anything is still unclear.
After upgrading a service to .NET 10, several developers saw squiggles under C# 14 extension members while CI was green. Their machines had no global.json constraint and still ran Visual Studio 2022. We added global.json, moved the team to Visual Studio 2026, and documented the minimum Rider version.
The interviewer is checking whether you understand that the IDE and the build are separate pipelines, and whether you troubleshoot methodically.
Green flags
global.json.Red flags
+----------------- IDE ------------------+
.csproj ------> | design-time build |
(+ Directory. | evaluate properties/items |
Build.props) | run selected targets (resolve refs) |--> language service --> squiggles
| results cached (.vs/, Rider caches) |
+----------------------------------------+
.csproj ------> dotnet build: restore -> full target graph -> csc --> real errors and outputs
A design-time build runs a subset of MSBuild targets to answer "which source files, references, analysers and compiler options does this project have?" without compiling to disk. It runs once per project and per target framework, its results are cached, and the language service builds its compilation model from them.
| Design-time build | Real build (dotnet build) |
|
|---|---|---|
| Purpose | Feed IntelliSense and analysis | Produce DLLs |
| Targets run | A subset (resolve references, collect compile items) | The full graph, including CoreCompile |
| Writes outputs | No | Yes, to bin/ and obj/ |
| Triggered | Opening the solution, changing a project file, switching branches | When you build |
| Cached | Yes, by the IDE | Incremental on disk |
Anything that makes these two views differ produces phantom errors (errors only the IDE shows) or missing ones.
| Cause | How it shows | How to confirm |
|---|---|---|
| IDE older than the SDK or C# version | New syntax underlined, CLI fine | Compare the IDE version with what .NET 10/C# 14 needs; dotnet --version in the repo |
| Different SDK in IDE and CLI | Random differences after an SDK install | dotnet --info in the repo folder; the IDE's toolset setting |
| Failed or partial restore | Missing types from packages, everything red | Missing or stale obj/project.assets.json; IDE restore output |
| Stale caches | Errors persist after a fix or a branch switch | Errors vanish after deleting .vs/ or invalidating caches |
| Wrong project context (multi-targeting) | API errors only for one framework | Switch the editor's project-context dropdown |
| Source generator not re-run | Generated members "missing" | Dependencies → Analyzers → (generator) shows old output |
| Conditional references or build-only targets | IDE misses files or references | Binary log of a design-time build |
| Wrong solution loaded | Projects missing from Solution Explorer | Title bar, Solution Explorer root, dotnet.defaultSolution in VS Code |
A target in Directory.Build.targets that generates source files only during a full build (for example BeforeTargets="CoreCompile" with a condition that skips design-time builds) means the IDE never sees those files. The real build compiles fine; the IDE complains about missing types.
MSBuild sets DesignTimeBuild=true (and Visual Studio sets BuildingInsideVisualStudio=true) during IDE evaluations. Conditions on those properties are a common culprit when you search the build files. The fix is to produce generated code with a source generator, or to make the generating target run in design-time builds too.
With <TargetFrameworks>net8.0;net10.0</TargetFrameworks>, the IDE analyses each framework separately but shows errors for the selected context (the dropdown at the top-left of the Visual Studio editor, or the equivalent in Rider). An API that exists only in net10.0 is a real error in the net8.0 context. The fix is #if NET10_0_OR_GREATER or a different API, not ignoring the squiggle.
MSBUILDDEBUGENGINE=1 before starting the IDE; MSBuild writes binary logs of every build, including design-time ones, to an MSBuild_Logs folder.dotnet build -bl log.Turn MSBUILDDEBUGENGINE off afterwards; it writes a lot of files and the logs can contain environment variables and paths.
dotnet build -bl.Slow design-time builds are also why large solutions feel sluggish. Heavy custom targets, many target frameworks and very large project counts all multiply design-time work. Solution filters and fewer target frameworks help.
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>.#if NET10_0_OR_GREATER and confirm both contexts are clean.Condition="'$(DesignTimeBuild)' != 'true'" that adds a generated .cs file, and see the IDE miss it while the build passes.dotnet build -bl and open msbuild.binlog in the MSBuild Structured Log Viewer.Diagnostic commands, run from the repo root:
dotnet --version # SDK chosen for this folder (honours global.json)
dotnet --list-sdks # SDKs installed on this machine
dotnet build-server shutdown
dotnet restore --force
dotnet build -bl # writes msbuild.binlog for the Structured Log Viewer
Remove build output folders (bash; review before running):
find . -type d \( -name bin -o -name obj \) -prune -exec rm -rf {} +
Pin the SDK so the IDE and CLI agree, in global.json at the repo root:
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestFeature"
}
}
Handling an API that only exists in one target framework:
namespace Orders.Domain;
public static class Hashing
{
public static string Sha256Hex(byte[] data)
{
#if NET10_0_OR_GREATER
return Convert.ToHexStringLower(System.Security.Cryptography.SHA256.HashData(data));
#else
return Convert.ToHexString(System.Security.Cryptography.SHA256.HashData(data)).ToLowerInvariant();
#endif
}
}
Mistake: making "restart Visual Studio" the whole fix for a recurring problem. A restart clears a one-off glitch, but if the errors come back it hides a real cause in the environment or the repo, and everyone keeps losing time. Instead: restart once; if it recurs, work through SDK, restore, caches and project configuration, and fix the cause in the repo.
Mistake: loading the wrong solution.
If a folder has several solutions, the IDE may load one that doesn't contain the project you're editing, so its references look missing.
Instead: check the title bar and Solution Explorer root in Visual Studio, the Solution view in Rider, and set "dotnet.defaultSolution": "Orders.slnx" in .vscode/settings.json for VS Code.
Mistake: overlooking IDE settings that change what is analysed. With full-solution analysis off, errors in closed files don't show; an Error List filter left on hides build errors. Instead: check the Error List filter and the analysis scope before concluding the IDE and CLI disagree.
Mistake: ignoring NuGet version differences.
A version that comes from a property, a Condition or a floating range (1.*) can resolve differently in the IDE's restore and the CLI's.
Instead: compare dotnet list package --include-transitive with obj/project.assets.json, and pin versions centrally with Central Package Management.
Mistake: running git clean -xdf blindly.
It deletes every untracked file, including local configuration and work in progress.
Instead: delete only bin/, obj/ and the IDE cache folder, or run git clean -xdn first to preview what would be removed.
Mistake: ignoring the project context dropdown in multi-targeted projects.
With net8.0 selected you see errors for APIs that exist only in net10.0, and miss errors that occur only in net10.0.
Instead: switch context to each framework, and rely on the CLI build, which compiles every target.
Mistake: not pinning the SDK.
Each machine resolves whatever SDK is newest, so the IDE and the CLI can use different compilers.
Instead: commit a global.json and check dotnet --version in the repo folder.
Mistake: assuming CI is wrong when it disagrees with the IDE.
CI builds from a clean clone, so it's usually the more trustworthy result.
Instead: reproduce the CI result locally with a clean clone and dotnet build -bl before blaming the pipeline.
Mistake: generating code in custom targets that only run in full builds.
The IDE never sees the generated files or references, so it reports missing types.
Instead: use a source generator, or make the target run in design-time builds too. Check generated files under Dependencies → Analyzers, and set EmitCompilerGeneratedFiles to true to compare them with the build output.
A senior engineer treats a recurring IDE-versus-CLI mismatch as a repository defect, not a personal annoyance. The fix belongs in the repo: global.json, a documented minimum IDE version, source generators instead of build-only code generation, and a CI build from a clean clone. Every minute developers spend deleting .vs folders is lost delivery time across the whole team.
Use evidence, not guesswork: the Error List filter tells you which side is wrong in seconds, and a binary log (-bl) shows exactly which SDK, properties and targets were used.
The dotnet host looks for a global.json from the current folder upwards. If there isn't one, it uses the latest installed SDK, even a preview. If there is one, it reads sdk.version and applies the rollForward policy to choose from the installed SDKs. With a version and no policy, the default is latestPatch, meaning the same feature band with the newest patch. allowPrerelease controls whether previews count. If nothing matches, every SDK command fails. I pin the SDK in every repo so developers, IDEs and CI build with the same compiler, analysers and defaults. dotnet --info shows which SDK was picked and why.
MSBuild is the build engine under dotnet build, Visual Studio and Rider, and a .csproj is an MSBuild file. Properties are named values such as $(Configuration). Items are lists with metadata, such as @(Compile) holding every .cs file. Targets are named steps, ordered with DependsOnTargets, BeforeTargets and AfterTargets. Each target runs tasks, the real units of work, such as Csc to compile or Copy. The build has two phases. Evaluation reads imports, properties and items. Execution runs the targets. Targets that declare Inputs and Outputs are skipped when nothing has changed, which is what makes builds incremental.
How to tell it (STAR)
**Situation:** After we moved our [order-processing service] to .NET 10, CI was green but about half the team had IntelliSense errors on the new C# 14 code. **Task:** I needed to find out why the IDE and CI disagreed and stop it recurring. **Action:** I used the Error List filter to show the errors were IntelliSense-only, compared `dotnet --version` with each developer's IDE version, and found they were still on Visual Studio 2022, which does not support .NET 10. I got everyone onto Visual Studio 2026 or a current Rider, added `global.json`, and documented minimum IDE versions. **Result:** The phantom errors disappeared within a day and we had no repeat on the next upgrade [add your own numbers, such as hours saved or tickets avoided].
Last updated 1 Nov 2026.