Frameworks · Go

AI code review for Go —
catches error-swallowing + goroutine leaks.

Go's simplicity is a feature until it hides bugs. Discarded errors (`_, _ := ...`), goroutines that outlive their context, panics used as control flow, string interpolation in database queries. LGTM's Go hints fire in the bugs + performance agents whenever it sees `go.mod` in your repo.

Auto-detected. Zero configuration.

Auto-detected when `go.mod` exists in your repo root. LGTM's Go hint pack loads into the bugs + performance + best-practices agents automatically.

Go bugs that hide in plain sight

Four patterns that get past general-purpose code review because they look like idiomatic Go. LGTM knows the shape of each.

Swallowed errors

`result, _ := someCall()` discards the error. Reviewers glance past it because the code compiles and often works. Until it doesn't — the discarded error was the only signal something went wrong.

Goroutine leaks

`go doThing()` with no context cancellation, no wait group, no way to stop the goroutine when the parent exits. Long-running services accumulate leaked goroutines that hold memory + connections forever.

panic as control flow

Go idiom is 'errors are values, not exceptions'. `panic()` is for genuinely unrecoverable state. Using it to bail out of a function because handling the error is annoying is anti-idiomatic and creates upstream crash sites.

database/sql string interpolation

`db.Exec(fmt.Sprintf("SELECT * FROM u WHERE id = '%s'", id))` is SQL injection. The `Exec` and `Query` methods take variadic args for a reason: `db.Query("SELECT ... WHERE id = $1", id)`. Easy to reach for Sprintf when the parameter placeholder syntax is unfamiliar.

What LGTM actually catches

Three real examples — the Go-aware bugs + security agents flag these with inline 🎯 Actionable comments and a clear "why" explanation.

criticalsql-injection
func getUser(db *sql.DB, id string) (*User, error) {
    row := db.QueryRow(fmt.Sprintf(
        "SELECT id, email FROM users WHERE id = '%s'", id,
    ))
    // ...
}

What: fmt.Sprintf building a SQL query from a user-supplied id

Why: Even if id looks like it comes from a trusted source, one refactor later it's from an HTTP request. Use parameter placeholders: `db.QueryRow("SELECT id, email FROM users WHERE id = $1", id)`.

highunhandled-error
func writeLog(msg string) {
    f, _ := os.OpenFile("app.log", os.O_APPEND|os.O_WRONLY, 0644)
    defer f.Close()
    f.WriteString(msg + "\n")
}

What: os.OpenFile error discarded — nil dereference on the very next line

Why: If OpenFile fails (permission denied, disk full), `f` is nil and `f.Close()` / `f.WriteString()` will panic. Handle the error: `if err != nil { return err }`, then use `f`.

highgoroutine-leak
func startBackgroundSync(user *User) {
    go func() {
        for {
            syncOnce(user)
            time.Sleep(1 * time.Minute)
        }
    }()
}

What: Goroutine spawned with no context or shutdown mechanism

Why: This goroutine runs forever — even after the User is deleted, the request is done, or the service tries to shut down gracefully. Accept a `ctx context.Context`, `select` on `ctx.Done()`, exit cleanly.

Go starter .lgtm.yml

Skip vendored deps + generated protobuf, force the go framework slug, bump SQL + goroutine-leak + unhandled-error to critical.

version: 1

paths:
  - pattern: "vendor/**"
    skip: true
  - pattern: "**/*.pb.go"
    skip: true
  - pattern: "**/*_generated.go"
    skip: true

frameworks:
  - "go"

severity_overrides:
  sql-injection: "critical"
  goroutine-leak: "critical"
  unhandled-error: "high"

auto_approve_docs_only: true

Commit as .lgtm.yml at your repo root, flip the Enable .lgtm.yml toggle in Repo Settings. Next PR picks it up. See all recipes →

What ships on every Go review

Same pipeline that reviews everything else on LGTM — the Gohints just tune each agent's attention.

6 agents in parallel

Bugs, Security, Performance, Readability, Best-practices, Documentation. Each with framework-specific hints when LGTM detects your stack.

16 CI/CD detectors

LGTM Security scans your GitHub Actions, Dockerfiles, and lockfiles alongside the code review. Merge-blocking Check Run on block-level findings.

Adversarial verifier

Every finding is refuted by a skeptic LLM before it posts. Only survivors reach your PR. Kills the confident-hallucination class of false positives.

Try LGTM on your Go repo

Free tier: 20 reviews / month, all 6 agents, all 16 security detectors. BYOK OpenAI or Anthropic. No card required.