# fatal error: sync: unlock of unlocked mutex

- **ID:** `go/mutex-lock-already-held`
- **Domain:** go
- **Category:** runtime_error
- **Verification:** ai_generated
- **Fix Rate:** 85%

## Root Cause

Calling Unlock() on a sync.Mutex that was not locked by the same goroutine, or double-unlocking a mutex.

## Version Compatibility

| Version | Status | Introduced | Deprecated |
|---------|--------|------------|------------|
| Go 1.18 | active | — | — |
| Go 1.19 | active | — | — |
| Go 1.20 | active | — | — |
| Go 1.21 | active | — | — |
| Go 1.22 | active | — | — |

## Workarounds

1. **Always pair Lock() and Unlock() in the same function, using defer immediately after Lock() to avoid mismatches** (95% success)
   ```
   Always pair Lock() and Unlock() in the same function, using defer immediately after Lock() to avoid mismatches
   ```
2. **Use sync.Mutex.TryLock() (Go 1.18+) to check if lock is held before unlocking, but prefer structured patterns** (85% success)
   ```
   Use sync.Mutex.TryLock() (Go 1.18+) to check if lock is held before unlocking, but prefer structured patterns
   ```

## Dead Ends

- **** — Defer runs even if the lock was never acquired (e.g., due to early return), causing an unlock of an unlocked mutex. (75% fail)
- **** — The Go runtime checks that the unlocking goroutine is the same one that locked it; cross-goroutine unlocks panic. (90% fail)
