A test leaked memory, and the leak was on the path that succeeded. The function allocated a scratch buffer, parsed into a result the caller owns, and released the scratch buffer with errdefer allocator.free(scratch). The error paths all freed it. The successful return didn't, and std.testing.allocator reported the leak when the test finished.
The reference is exact about this. errdefer runs its expression on block exit if and only if the function returned with an error. A plain return is not an error return.
errdefer fits a different case, a buffer that belongs to the caller after success and has to be released only on failure. The scratch buffer belongs to the function on every path. That's defer.
So the keyword records who owns the allocation after a successful return. errdefer says the caller does. defer says nobody outside the function does. Choosing one is an ownership decision, not a matter of cleanup style. C hides the same choice in which label the success path jumps past in a goto cleanup block.