Defer in Loop

ID

go.defer_in_loop

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Go

Tags

reliability, suspicious

Description

Reports a defer statement whose nearest enclosing loop is inside the same function. A deferred call is not executed at the end of the loop body — it is queued and only runs when the enclosing function returns.

Rationale

A resource opened on every iteration and released with defer stays open until the whole function finishes, because the deferred call never fires between iterations. Over a large or unbounded loop this accumulates open file descriptors, held locks or connections and can exhaust the process. A defer inside a closure that is itself defined inside the loop is not reported — the closure owns its own defer scope, so its deferred calls run per closure invocation, which is the correct per-iteration cleanup idiom.

for _, name := range names {
    f, _ := os.Open(name)
    defer f.Close() // FLAW — every file stays open until the function returns
}

for _, name := range names {
    func() {
        f, _ := os.Open(name)
        defer f.Close() // OK — the closure releases the file each iteration
    }()
}

Remediation

Release the resource at the end of the loop body with an explicit call, or wrap the per-iteration work in a closure (or a helper function) so the defer fires when that closure returns.