Reference
Troubleshooting
Symptom → cause → fix for the common issues after obfuscation: missing members, serialization, XAML binding, reflection, SmartScreen, strong names and more.
Most issues come down to one thing — something resolved by name at runtime was renamed. This page maps symptoms to fixes. If yours isn’t here, contact us with the exception and your config.
How to find the culprit fast (bisect)
When a protected build misbehaves, narrow it down by turning passes off one at a time and re-testing:
- Rename off (
"renameIdentifiers": false) — if the problem disappears, it’s a name resolved at runtime → preserve it (see below). This is by far the most common cause. - Control-flow off (
"controlFlowObfuscation": false) — if that fixes it, tell us with the method; control-flow is behaviour-preserving by design, so we want to see it. - Strings off (
"encryptStrings": false) — rarely the cause; strings decrypt to identical values.
Whichever pass “fixes” it tells you the category. Then re-enable it and apply a targeted exclusion instead of leaving the pass off.
Runtime errors
| Symptom | Cause | Fix |
|---|---|---|
MissingMethodException / MissingFieldException / TypeLoadException at runtime | A member found by reflection/name was renamed | Preserve it: exclude, [System.Reflection.Obfuscation], or preservePublicApi. See Keeping your app working. |
Type.GetType("MyApp.Foo") returns null | The type was renamed | Preserve that type (exclude), or change the code to typeof(Foo) (identity, rename-safe). |
| JSON/XML has odd field names, or deserialization returns nulls | DTO property names were renamed | Preserve the DTOs (attributes are auto-detected; otherwise exclude). |
| WPF/MAUI: binding errors in the log, blank or dead UI | View-model or bound members renamed | Preserve view-models + bound members; keep preservePublicApi. |
| DI: “unable to resolve service” / convention registration finds nothing | Assembly-scanning by name hit renamed types | Preserve the scanned types, or register by type. |
| EF Core: wrong columns / migration mismatch | Entity property names renamed | Preserve entities, or use explicit HasColumnName. |
TypeInitializationException mentioning a Nebula trial | The assembly was built with a trial license | Rebuild with a paid license — trial builds warn and stop after the trial date by design. |
Distribution & signing
| Symptom | Cause | Fix |
|---|---|---|
| “Windows protected your PC” / “Unknown publisher” (SmartScreen) | The installer/exe isn’t code-signed yet | Sign with an Authenticode certificate, or install via the Microsoft Store (Microsoft-signed). Reputation also builds over time. See Installation. |
| Antivirus flags a freshly obfuscated binary | New/unsigned binaries have no reputation | Sign it (Authenticode), submit a false-positive report to the vendor if needed. |
| Strong-named assembly fails to load / “signature not valid” | Obfuscation rewrote the assembly after signing | Let Nebula re-sign: set strongNameKeyFile (signing happens after obfuscation). |
| Single-file build not tamper-protected | Anti-tamper’s self-hash no-ops in a single-file bundle (no on-disk path) | Expected — use control-flow + string encryption there, or ship multi-file to keep anti-tamper. |
Build / CI
| Symptom | Cause | Fix |
|---|---|---|
| Build integration does nothing on a dev box | NebulaObfuscate isn’t set there (by design) | Set it only on the build agent — see MSBuild & CI. |
| “build integration is a licensed feature” | Free edition is CLI/GUI only | Provide a license on the build machine (NEBULA_LICENSE, licenseFile, or NebulaLicenseFile). |
| Non-zero exit / config error in CI | Bad config or missing input | Check the exit code and the log; validate paths in nebula.config.json. |
Reading an obfuscated crash
A production stack trace full of renamed names can be translated back with the symbol map from the build:
nebula deobfuscate --map protected/MyApp.symbols.json --input crash.txt
See De-obfuscating stack traces. Keep the *.symbols.json from each release build for exactly this.
Diagnosing why a method didn’t flatten
Control-flow leaves a method un-flattened only when it can’t prove the transform is safe (it never emits broken IL). To see why, set the environment variable and run the CLI:
NEBULA_CF_DEBUG=1
It prints, per method, whether each scope flattened or why it was skipped — useful when you expected a specific method (e.g. a license check) to be flattened.
Performance
Renaming and string encryption have negligible runtime cost. Control-flow flattening adds some overhead per transformed method, so for hot paths scope it with controlFlowInclude/controlFlowExclude or a lower controlFlowIntensity rather than flattening everything.