Fail-Closed Error Propagation and Elimination of Swallowed Errors
Overview
Historically, certain critical paths across synchronization loops and persistent index caches assigned error returns to blank identifiers (_ = sp.Update(...)) or recorded errors through mechanical logging helpers (logging.LogSwallowedError(...)) while allowing execution to proceed.
This failure mode created severe silent corruption risks (TDE-F-CQ-001):
- Silent Lock Timeout Failures: In
pkg/storage/reverse_reference_index_persist.go, lock acquisition timeouts during cache loading or saving were swallowed, leading callers to assume clean persistence when in fact cache updates failed. - Transaction Rollback Loss: In
cmd/zqk/agent/sync_loop.go, failure to create audit stamps or apply mutations silently ignored rollback errors.
This specification documents the fail-closed error propagation architecture implemented to eradicate silent failure modes.
Architectural Principles
1. Fail-Closed Concurrency Locks
When acquiring read or write locks with timeouts via concurrency.WithLockTimeout or concurrency.WithRLockTimeout:
- Timeouts MUST return an explicit error wrapped with context.
- Under no circumstance may lock acquisition timeout errors be swallowed or substituted with boolean success flags.
var errLock = concurrency.WithLockTimeout(&r.mu, ctx, nil, func() error {
// critical section
return nil
})
if errLock != nil {
return false, errfmt.Errorf("reverse reference index lock timeout during load: %w", errLock)
}
2. Explicit Transaction Error Joining
When performing atomic operations within transactional storage scopes:
- Rollback operations on failure MUST be checked.
- If rollback encounters an error, the primary failure and the rollback failure MUST be joined via
errors.Join(primaryErr, rollbackErr).
3. Structured Observability via Fluent Logging
Where non-fatal events require warning logs, errors MUST be attached via .WithError(err) on the fluent logger builder (logging.FluentEvent(logger).Warn(...).WithError(err).Log()) rather than swallowing or lossy string formatting.
Verification and Test Coverage
TestReverseReferenceIndex_SaveCache_LockContention_PropagatesError: Verifies that lock acquisition timeouts inReverseReferenceIndex.SaveCachefail closed and return an explicit error containinglock timeout.TestApplyStateMutation_ErrorPropagation: Verifies that mutation failures in the agent sync loop fail closed and propagate non-nil errors to the coordinator.