Troubleshooting
Symptom -> cause -> fix, grouped by where it bites. Exit-code meanings are in
19-cli-reference.md.
Build failures
| Symptom |
Cause |
Fix |
dotnet build fails with MSB3027/MSB3021 copy errors |
The app is running (especially under the VS debugger) and its output DLLs are locked. |
Stop the debugger, or taskkill //F //IM GridSim.Wpf.exe, then rebuild. To compile-check without disturbing a running instance: dotnet build ... -p:BaseOutputPath=obj/verifybin/ (delete obj/verifybin afterwards). |
Build fails on a warning (error CA... / error CS...) that looks cosmetic |
Directory.Build.props sets TreatWarningsAsErrors with the .NET analyzers in Recommended mode - the repo builds at 0 warnings / 0 errors by policy. |
Fix the warning; do not suppress it inline. If a CA rule is genuinely noise for numerical code, the tuning point is the repo-root .editorconfig, not a pragma. |
Power flow does not converge
Not every DID NOT CONVERGE is a bug. Three input classes are expected not to
converge (the canonical list; details and evidence in docs/benchmarking.md sec. 5):
| Symptom |
Cause |
Fix |
A pglib_opf_case* shows **NO** at 100 iterations, while its non-OPF twin (e.g. case2848rte) solves in 2 |
PGLib-OPF files are optimisation solutions, not valid flat-start power flows. |
Expected. Bench the non-OPF twin, or accept the NO as an input property. |
A small radial feeder (case33bw, case69, case85, case141, ...) diverges under Newton-Raphson |
Radial distribution networks need the backward/forward-sweep solver, not the transmission NR. |
Run with --bfs (auto-selected when the model looks radial). Known limitation: BFS is exact only at unity tap. |
LOAD FAIL: The input string '135/sqrt(3)' was not in a correct format (case533mt_*) |
The MATPOWER parser does not evaluate arithmetic expressions in data matrices. |
Parser gap, not a solver failure. Pre-evaluate the expression in the .m file if you need the case. |
Dense engine reports OOM on a large case |
The dense path is memory-bound above ~600 buses (9,241 buses OOMs an 8 GB box). |
Use the sparse engine - it solves the same case. Cap the heap (DOTNET_GCHeapHardLimit) so sweeps fail catchably. |
| A case that used to converge now does not |
Genuine regression. |
Run gridsim crossval <case> and tools/crossval/residual_check.py on the export before touching solver code - they tell you whether the last good solution was real. |
State estimation
| Symptom |
Cause |
Fix |
observability: UNOBSERVABLE, exit 3 |
Measurement coverage is too thin to pin every state - the console lists the unmeasured island buses and free state directions. |
Add measurements where it says: a Vm or injection measurement on each listed bus, or a flow on a branch into the unobserved region. With --synth, raise --coverage-vm/--coverage-inj/--coverage-flow. On real GB sets, the pseudo-measurement builders exist precisely to close these gaps. |
consistency: J = ... vs chi2(95%) = ... -> FAIL |
The measurements are mutually inconsistent at their declared sigmas: a gross error survived, sigmas are optimistic, or the model does not match the era of the telemetry. |
Look at what bad-data removal fingered: the removed block of estimate-export.json (or removed_json in the GDA estimated-state lake rows) and the worst rows of residuals.csv. Recurrent offenders at reactive-compensated sites (the QInjMvar outlier league) point at model fidelity, not telemetry. If you enabled SPINE_ETYS=1, that era mismatch alone regresses chi-square - turn it off (16-configuration.md). |
estimate on real data diverges or crawls from flat start |
Real operating points are far from flat. |
Pass --warm-start (or --prior solve); this is standard EMS practice and the documented requirement for real data. |
| Every removal makes things worse in a weak area |
Removal in a weakly-redundant region lets the state drift from the prior. |
Use --backfill-removals - each removed measurement is replaced by a loose prior-evaluated pseudo. |
Bridge (WPF/web <-> GDA)
| Symptom |
Cause |
Fix |
Status pill: CONNECT FAILED * ... |
No bridge server listening at the configured endpoint, or the endpoint is wrong. |
Start the GDA bridge (Applications/GridSim-Bridge/bridge_server.py) and check BridgeEndpoint in %LOCALAPPDATA%\GridSim\settings.json (WPF) or GridSim:BridgeEndpoint (web) - default ws://127.0.0.1:8770/ws/bridge. |
Status pill: HORIZON REJECTION * stream stopped |
You asked for (or the stream reached) a timestamp newer than the no-forward-inference cutoff - the bridge replays settled history only (cutoff ~ now - 7 d 1 h). |
Pick an earlier start: the --back launch arg or the start picker in the WPF app; an earlier date in the web session. This is an invariant, not a fault. |
Info message: No presolved/estimated frames for '<case>' in <window>; streaming the live solve instead |
The stored lake window you asked for has not been built; the bridge falls back to on-demand solving (you lose per-bus frame-by-frame replay, not the view). |
Build the window: NetworkModel/presolve_lake.py for presolved, NetworkModel/build_estimated_state.py for estimated. |
SRC: estimated shows nothing for a recent day |
The estimated-state lake is built day by day; that day has not been estimated yet. |
Run the daily task (build_gsp_measurements -> build_estimated_state for the date) - see 11-estimating-gb-history.md - then reconnect. |
ESTIMATED * ... * chi-square FAIL on the status pill |
Not a bridge fault: the stored estimate for that period failed consistency and the pill is honestly saying so. |
Treat as the chi-square FAIL row above - inspect that period's removed_json/residuals in the lake. |
BRIDGE FAULT * disconnected mid-stream |
The receive loop faulted (server restarted, network drop). |
Reconnect; check the bridge server console for the underlying error. |
GUI
| Symptom |
Cause |
Fix |
| Map draws every bus at one point (bottom-left, the Cornwall corner) |
The streamed topology carries no x/y layout coordinates - an old bridge server predating the layout fields in the topology block. |
Update/restart the GDA bridge server so its topology export populates bus x/y; local JSON cases need X/Y on their buses. |
| Loaded a run directory but tabs show computed, not recorded, telemetry |
The run has no sidecar timeseries.csv, so replay computes what it can. |
Re-run transient with logging on (default) or accept computed values. |
See also