Debugging contributor failures
Begin with the smallest repeatable failure. Save the exact command, target framework, layout, options, variables, input bytes, starting stream position, exception type/code/path/offset, and current Git revision. Changing several of those at once makes it hard to know which assumption was wrong.
First identify the stage
| Failure appears during | Inspect first |
|---|---|
CStruct construction |
Layout exception code/message, unsupported syntax, names/types, expressions, compilation limits |
| Path selection | Root/member case, array index, pointer .address/.value depth |
| Read or typed mapping | Starting position, byte order, placement, exact payload length, direct result before mapped-class conversion |
| Configured limit | The specific option and which array/string/byte/nesting/pointer work consumed it |
| Pointer traversal | Stored address, pointer width, absolute/relative mode, origin, target range, cycle/depth limits |
| Write | Input shape, missing/null member, numeric range, string/array size, union selection, destination capability |
| Update | Path-location read versus replacement write, validation failure versus physical commit failure |
| Documentation | Wrapper output, DocFX log, broken source/link, stale core assembly, Node/browser prerequisite |
Run only the focused reproducer until you understand the cause. Then rerun the broader check that originally found the problem.
SDK and restore problems
From the repository root:
dotnet --info
dotnet --version
dotnet --info lists installed SDKs and the selected environment. dotnet --version should be a stable .NET 10 SDK
selected by global.json. The resolver message “there was no version specified” usually means no installed stable
.NET 10 SDK satisfies that file. Install the SDK rather than changing target frameworks to fit the machine.
To find the NuGet package cache:
dotnet nuget locals global-packages --list
This prints the cache location for inspection. Do not copy cache contents into the repository or commit them as a restore fix.
Layout and byte failures
For a layout error:
- Reduce the source to the smallest declaration that still fails.
- Compare it with Differences from C.
- Check duplicate names, unknown types, by-value recursion, enum backing, array/bitfield expressions, and limits.
- Keep the
InvalidLayoutcode in a focused regression.
For a wrong value or offset:
- Record the exact input in hexadecimal.
- Draw the expected offset and width of each field.
- Confirm little/big byte order and packed/aligned placement.
- Check the stream's position before the operation.
- Use
ParseWithDebugorResolveAddressonly after the simple calculation is explicit.
For a typed-mapping failure, read the path without <T> first. If the direct value is wrong, debug binary decoding.
If it is right, inspect the mapper's ReadFrom: the member names it asks for, the Get<T> target types, nullability, and numeric ranges.
Update failures
Compare the complete destination bytes and stream position before and after the call. A path, traversal, conversion, shape, pointer, union, or configured-limit failure should occur during staging and leave both unchanged.
A custom stream can fail during the final physical commit after accepting some bytes. Preserve the original inner exception and distinguish that storage failure from a validation bug. Do not claim generic rollback that the stream does not provide.
Replay fuzz and property failures
The harness prints the target, seed, iteration, and minimized input. Use those exact values to reproduce the failure. Turn the minimized input into a named focused test before changing the implementation. After the fix, confirm both the named regression and the original seed.
If a property test fails, write down which equality it expects. Meaningful round-trip equality may allow normalized padding while byte-for-byte union preservation has a stricter rule.
Coverage and mutation failures
Coverage and mutation reports are generated from a particular DLL/PDB and target framework. Confirm they match the source you are inspecting before interpreting a line number.
For a surviving mutation:
- Read the changed condition or value.
- Identify the public behavior that should differ.
- Add an assertion that fails with the mutation.
- Rerun the focused mutation set.
Separate a real survivor from “not covered,” compile failure, or an instrumentation limitation. Record a narrow reviewed limitation when tooling genuinely cannot instrument the code; do not use a broad exclusion.
Documentation failures
Run the documented wrapper rather than a partial DocFX command:
node tools/documentation/validate-documentation.mjs
The wrapper prints each subcommand and stops at the first failed stage. A stale-core message means the docs were
started with --no-build after a relevant source change; run the full validator once. Browser failures include the
page, assertion, console output, and accessibility finding.
After fixing the focused cause, return to Testing and quality checks and rerun the affected wider gate.