Skip to content
Ownership and lifecycle

deferinloop

Checks cleanup defers whose lifetime extends across loop iterations.

Fix offeredNo

What it detects

A defer inside a loop runs when the surrounding function returns, not when the iteration ends. Put cleanup-sensitive work in a helper function so files, locks, timers, and similar resources are released after each iteration.

Checks

Check What it detects
cleanup-lifetime
Reports cleanup defers whose lifetime extends across loop iterations.

Why this is flagged

Deferring cleanup until the whole function returns lets resources accumulate across iterations. A large or long-running loop can then exhaust file handles, hold locks too long, or keep timers and connections alive unnecessarily.

Further reading: Effective Go: Defer.

How to fix it

Move the body of one iteration into a small helper function and defer cleanup there. The helper returns at the end of each iteration, so its resources are released before the next iteration starts.

Examples

Flagged code

func readAll(names []string) error {
for _, name := range names {
file, err := os.Open(name)
if err != nil {
return err
}
// gohawk: deferred cleanup runs after the loop instead of after this iteration
defer file.Close()
}
return nil
}

Accepted code

func readAllSafely(names []string) error {
for _, name := range names {
if err := readOne(name); err != nil {
return err
}
}
return nil
}
func readOne(name string) error {
file, err := os.Open(name)
if err != nil {
return err
}
defer file.Close()
return nil
}