resourcelifetime
Checks owned files, SQL handles, HTTP responses, timers, and compressors are released on every path.
What it detects
Release owned resources on every path. Storing a resource in a partially constructed object does not transfer ownership when an error path returns without that object.
The built-in contracts cover files, transactions, SQL rows and statements, HTTP response bodies, timers and tickers, and gzip/zlib readers and writers.
Checks
| Check | What it detects |
|---|---|
missing-release |
Reports owned resources that are not released on every return path. |
Why this is flagged
Owned resources are limited and often hold work open elsewhere. Missing cleanup on even one return path can leak file descriptors, database connections, network bodies, timers, or transactions until the program slows down or fails.
Further reading: Effective Go: Defer, Package net/http.
How to fix it
As soon as a resource is acquired successfully, arrange for its matching
cleanup on every later return path. Use defer when the current function owns
the resource, or transfer it only when the receiver clearly takes ownership.
Examples
Flagged code
Leaked file
func read(path string) error { // gohawk: owned resource from os.Open is not released on every return path file, err := os.Open(path) if err != nil { return err } _ = file return nil}Leaked database rows
func query(ctx context.Context, database *sql.DB) error { // gohawk: owned resource from sql.QueryContext is not released on every return path rows, err := database.QueryContext(ctx, "SELECT 1") if err != nil { return err } _ = rows return nil}Accepted code
func readSafely(path string) error { file, err := os.Open(path) if err != nil { return err } defer file.Close() return nil}Options
| Knob | Default | Effect |
|---|---|---|
contracts |
os,http,sql,time,compress |
Comma-separated resource contract families: os,http,sql,time,compress. |
require-reader-close |
true |
Require gzip and zlib readers to be closed. |