Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

golang-safety — Claude Code skill: prevent nil panics in Go

Skill prevents common Go crashes before they happen: nil map writes, typed-nil interfaces, append aliasing, silent integer truncation, and resource leaks from defer in loops.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add samber/cc-skills-golang --skill golang-safety -a claude-code
Open on GitHub

What golang-safety does

golang-safety teaches defensive Go: catching mistakes that turn into panics at runtime. The skill covers nil safety (interfaces, maps, slices), append aliasing when two slices share a backing array, defensive copying for exported data, defer inside loops that accumulate resources, and float comparison traps. Each issue comes with code examples and fixes.

You get a defensive engineer persona that sees every untested assumption about nil, capacity, and numeric range as a latent crash. The skill focuses on normal programmer mistakes, not exploitable vulnerabilities — for concurrency and goroutines, use golang-concurrency; for attackers and weak crypto, use golang-security.

What it helps with

  • prevent panic from writing to a nil map or dereferencing a nil pointer
  • catch typed-nil interfaces that are not equal to nil
  • avoid silent truncation when converting between integer types
  • protect struct internals from mutation by callers
  • find defer inside loops that holds resources open until function exit

Example prompts for the agent

  • Review this function for nil safety, especially where we return interfaces
  • Find all places where we write to a map without checking if it's initialized
  • Does this code have append aliasing where two slices share the backing array

How to install golang-safety

1Skills CLI (Vercel)

npx skills add samber/cc-skills-golang --skill golang-safety -a claude-code

2Manually: put the files in place

~/.claude/skills/golang-safety/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/golang-safety/

From the same set32

The whole set →
golang-code-style samber A skill for writing clear Go code: line breaking, variable declarations, control flow, when comments help. Use it when writing or reviewing Go, discussing style, or setting project standards. Claude Code CX Codex Cursor +1 44,000installs golang-error-handling samber Skill for idiomatic Go error handling: wrapping with context, chain inspection, structured logging, and samber/oops integration. Apply when creating, wrapping, inspecting, or logging errors in Go code. Claude Code CX Codex Cursor +1 43,200installs golang-security samber Identifies Go vulnerabilities, models threats with STRIDE and DREAD, and fixes injections, secret leaks, and cryptography flaws. Claude Code CX Codex Cursor +1 42,100installs golang-design-patterns samber This skill guides idiomatic Go design for APIs, resource lifecycles, graceful shutdown, and architecture. Use it when designing or reviewing Go code. Claude Code CX Codex Cursor +1 41,900installs golang-performance samber Guides AI coding agents through profiling, memory allocation cuts, and CPU optimization in Go projects using a strict measurement-first cycle. Claude Code CX Codex Cursor +1 41,800installs golang-concurrency samber The skill sets rules for concurrent Go code: goroutine lifecycle, channels and select, mutexes, errgroup, singleflight, worker pools, and fan-out/fan-in pipelines. Claude Code CX Codex Cursor +1 41,600installs golang-database samber Go database guidelines for database/sql, sqlx, and pgx. Enforces parameterized queries, context passing, and explicit connection pool handling without ORMs. Claude Code CX Codex Cursor +1 40,700installs golang-naming samber A Go naming skill for writing, reviewing, and refactoring code. It covers packages, types, errors, booleans, enums, and tests. Claude Code CX Codex Cursor +1 40,700installs

Similar

gdpr-data-handling Seth Hobson Guides GDPR data handling with consent management, data subject rights, and privacy by design. Use it when a system processes EU personal data. Claude Code CX Codex Cursor 14,862installs voicestudio-maintainer debpalash Guides maintenance tasks for VoiceStudio by triaging issues, reviewing PRs, diagnosing CI runs, and staging Electron releases. It is not designed for audio synthesis. Claude Code CX Codex Cursor +1 795installs security-review official github Reviews code and dependencies for vulnerabilities, exposed secrets, and access-control flaws. Use it before a release or when auditing a project path. GitHub Copilot 5,500installs improve-codebase-architecture Matt Pocock This skill scans a codebase for architectural friction, then builds a visual HTML report with refactoring candidates. Use it before a deeper architecture review. Claude Code 1,163,035installs tdd Matt Pocock Guides the red-green TDD loop: ensures tests are written first, keep them at public interfaces, and avoid coupling to implementation details. Claude Code 1,141,062installs triage Matt Pocock This skill moves issues and external PRs through triage states, verification, and agent briefs. It fits maintainers managing an issue queue. Claude Code 990,453installs codebase-design Matt Pocock This skill helps design modules with small interfaces and hidden complexity. Use it when refactoring code, choosing a seam, or making tests easier to write. Claude Code 850,344installs code-review Matt Pocock Reviews changes from a chosen reference point against repository standards and the originating spec. Use it for a branch, PR, or work in progress. Claude Code 789,521installs

Questions about golang-safety

How do I install golang-safety in Claude Code?

Run npx skills add samber/cc-skills-golang --skill golang-safety -a claude-code, then open ~/.claude/skills/golang-safety/SKILL.md.

What does golang-safety do?

Helps you write defensive Go code by catching common mistakes: panics on nil maps, typed-nil interfaces, silent integer truncation, append aliasing, and defer resource accumulation.

When should I use golang-safety versus other skills?

Use it when reviewing code for nil safety or designing types with safe zero values. For concurrency with goroutines, use golang-concurrency; for vulnerabilities like injection, use golang-security.

Source text

SKILL.md · MIT
---
name: golang-safety
description: "Defensive Golang coding against accidental bugs — nil panics, typed-nil interfaces, `append` backing-array aliasing, silent int64-to-int32 truncation, float `==` comparison, `defer` inside loops, defensive copies of slices and maps, and usable zero values. Use when a Go program panics on a nil map write or nil pointer dereference, when reviewing code for nil-safety, numeric conversion overflow, or resource lifecycle, or when designing a type whose zero value must be safe. Not for designing concurrent access with goroutines, channels, or sync primitives (→ See `samber/cc-skills-golang@golang-concurrency` skill), not for exploitable vulnerabilities such as injection, weak crypto, or leaked secrets (→ See `samber/cc-skills-golang@golang-security` skill), and not for debugging an already-failing program (→ See `samber/cc-skills-golang@golang-troubleshooting` skill)."
user-invocable: true
license: MIT
compatibility: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
metadata:
  author: samber
  version: "1.3.2"
  openclaw:
    emoji: "🛡"
    homepage: https://github.com/samber/cc-skills-golang
    requires:
      bins:
        - go
    install: []
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*) Bash(git:*) Agent
paths:
  - "**/*.go"
---

**Persona:** You are a defensive Go engineer. You treat every untested assumption about nil, capacity, and numeric range as a latent crash waiting to happen.

# Go Safety: Correctness & Defensive Coding

Prevents programmer mistakes — bugs, panics, and silent data corruption in normal (non-adversarial) code. Security handles attackers; safety handles ourselves.

## Best Practices Summary

1. **Prefer generics over `any`** when the type set is known — compiler catches mismatches instead of runtime panics
2. **Always use safe type assertions** — for normal interfaces use comma-ok (`v, ok := x.(T)`); for reflection in Go 1.25+ prefer `reflect.TypeAssert[T](value)` over `value.Interface().(T)`.
3. **Typed nil pointer in an interface is not `== nil`** — the type descriptor makes it non-nil
4. **Writing to a nil map panics** — always initialize before use
5. **`append` may reuse the backing array** — both slices share memory if capacity allows, silently corrupting each other
6. **Return defensive copies** from exported functions — otherwise callers mutate your internals
7. **`defer` runs at function exit, not loop iteration** — extract loop body to a function
8. **Integer conversions truncate silently** — `int64` to `int32` wraps without error
9. **Float arithmetic is not exact** — use epsilon comparison or `math/big`
10. **Design useful zero values** — nil map fields panic on first write; use lazy init
11. **Use `sync.Once` for lazy init** — guarantees exactly-once even under concurrency

## Nil Safety

Nil-related panics are the most common crash in Go.

### The nil interface trap

Interfaces store (type, value). An interface is `nil` only when both are nil. Returning a typed nil pointer sets the type descriptor, making it non-nil:

```go
// ✗ Dangerous — interface{type: *MyHandler, value: nil} is not == nil
func getHandler() http.Handler {
    var h *MyHandler // nil pointer
    if !enabled {
        return h // interface{type: *MyHandler, value: nil} != nil
    }
    return h
}

// ✓ Good — return nil explicitly
func getHandler() http.Handler {
    if !enabled {
        return nil // interface{type: nil, value: nil} == nil
    }
    return &MyHandler{}
}
```

### Nil map, slice, and channel behavior

| Type    | Index into nil | Write to nil   | Len/Cap of nil | Range over nil |
| ------- | -------------- | -------------- | -------------- | -------------- |
| Map     | Zero value     | **panic**      | 0              | 0 iterations   |
| Slice   | **panic**      | **panic**      | 0              | 0 iterations   |
| Channel | Blocks forever | Blocks forever | 0              | Blocks forever |

```go
// ✗ Bad — nil map panics on write
var m map[string]int
m["key"] = 1

// ✓ Good — initialize or lazy-init in methods
m := make(map[string]int)

func (r *Registry) Add(name string, val int) {
    if r.items == nil { r.items = make(map[string]int) }
    r.items[name] = val
}
```

See **[Nil Safety Deep Dive](./references/nil-safety.md)** for nil receivers, nil in generics, and nil interface performance.

## Slice & Map Safety

### Slice aliasing — the append trap

`append` reuses the backing array if capacity allows. Both slices then share memory:

```go
// ✗ Dangerous — a and b share backing array
a := make([]int, 3, 5)
b := append(a, 4)
b[0] = 99 // also modifies a[0]

// ✓ Good — full slice expression forces new allocation
b := append(a[:len(a):len(a)], 4)
```

### Map concurrent access

Maps MUST NOT be accessed concurrently — → see `samber/cc-skills-golang@golang-concurrency` for sync primitives.

See **[Slice and Map Deep Dive](./references/slice-map-safety.md)** for range pitfalls, subslice memory retention, and `slices.Clone`/`maps.Clone`.

## Numeric Safety

### Implicit type conversions truncate silently

```go
// ✗ Bad — silently wraps around if val > math.MaxInt32 (3B becomes -1.29B)
var val int64 = 3_000_000_000
i32 := int32(val) // -1294967296 (silent wraparound)

// ✓ Good — check before converting
if val > math.MaxInt32 || val < math.MinInt32 {
    return fmt.Errorf("value %d overflows int32", val)
}
i32 := int32(val)
```

### Float comparison

```go
// ✗ Bad — floating point arithmetic is not exact
var a, b, c float64 = 0.1, 0.2, 0.3
a+b == c // false

// ✓ Good — use epsilon comparison
const epsilon = 1e-9
math.Abs((a+b)-c) < epsilon // true
```

### Division by zero

Integer division by zero panics. Float division by zero produces `+Inf`, `-Inf`, or `NaN`.

```go
func avg(total, count int) (int, error) {
    if count == 0 {
        return 0, errors.New("division by zero")
    }
    return total / count, nil
}
```

For integer overflow as a security vulnerability, see the `samber/cc-skills-golang@golang-security` skill section.

## Resource Safety

### defer in loops — resource accumulation

`defer` runs at _function_ exit, not loop iteration. Resources accumulate until the function returns:

```go
// ✗ Bad — all files stay open until function returns
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close() // deferred until function exits
    process(f)
}

// ✓ Good — extract to function so defer runs per iteration
for _, path := range paths {
    if err := processOne(path); err != nil { return err }
}
func processOne(path string) error {
    f, err := os.Open(path)
    if err != nil { return err }
    defer f.Close()
    return process(f)
}
```

### Goroutine leaks

→ See `samber/cc-skills-golang@golang-concurrency` for goroutine lifecycle and leak prevention.

## Immutability & Defensive Copying

Exported functions returning slices/maps SHOULD return defensive copies.

### Protecting struct internals

```go
// ✗ Bad — exported slice field, anyone can mutate
type Config struct {
    Hosts []string
}

// ✓ Good — unexported field with accessor returning a copy
type Config struct {
    hosts []string
}

func (c *Config) Hosts() []string {
    return slices.Clone(c.hosts)
}
```

## Initialization Safety

### Zero-value design

Design types so `var x MyType` is safe — prevents "forgot to initialize" bugs:

```go
var mu sync.Mutex   // ✓ usable at zero value
var buf bytes.Buffer // ✓ usable at zero value

// ✗ Bad — nil map panics on write
type Cache struct { data map[string]any }
```

### sync.Once for lazy initialization

```go
type DB struct {
    once sync.Once
    conn *sql.DB
}

func (db *DB) connection() *sql.DB {
    db.once.Do(func() {
        db.conn, _ = sql.Open("postgres", connStr)
    })
    return db.conn
}
```

### init() function pitfalls

→ See `samber/cc-skills-golang@golang-design-patterns` for why init() should be avoided in favor of explicit constructors.

## Enforce with Linters

Many safety pitfalls are caught automatically by linters: `errcheck`, `forcetypeassert`, `nilerr`, `govet`, `staticcheck`. See the `samber/cc-skills-golang@golang-lint` skill for configuration and usage.

### Go 1.25+ reflection type assertions

For reflection code, prefer `reflect.TypeAssert[T]` over `value.Interface().(T)`.

```go
v := reflect.ValueOf(x)
if s, ok := reflect.TypeAssert[string](v); ok {
    use(s)
}
```

## Common Mistakes

| Mistake | Fix |
| --- | --- |
| Bare type assertion `v := x.(T)` | Panics on type mismatch, crashing the program. Use `v, ok := x.(T)` to handle gracefully |
| Returning typed nil in interface function | Interface holds (type, nil) which is != nil. Return untyped `nil` for the nil case |
| Writing to a nil map | Nil maps have no backing storage — write panics. Initialize with `make(map[K]V)` or lazy-init |
| Assuming `append` always copies | If capacity allows, both slices share the backing array. Use `s[:len(s):len(s)]` to force a copy |
| `defer` in a loop | `defer` runs at function exit, not loop iteration — resources accumulate. Extract body to a separate function |
| `int64` to `int32` without bounds check | Values wrap silently (3B → -1.29B). Check against `math.MaxInt32`/`math.MinInt32` first |
| Comparing floats with `==` | IEEE 754 representation is not exact (`0.1+0.2 != 0.3`). Use `math.Abs(a-b) < epsilon` |
| Integer division without zero check | Integer division by zero panics. Guard with `if divisor == 0` before dividing |
| Returning internal slice/map reference | Callers can mutate your struct's internals through the shared backing array. Return a defensive copy |
| Multiple `init()` with ordering assumptions | `init()` execution order across files is unspecified. → See `samber/cc-skills-golang@golang-design-patterns` — use explicit constructors |
| Blocking forever on nil channel | Nil channels block on both send and receive. Always initialize before use |

## Cross-References

- → See `samber/cc-skills-golang@golang-concurrency` skill for concurrent access patterns and sync primitives
- → See `samber/cc-skills-golang@golang-data-structures` skill for slice/map internals, capacity growth, and container/ packages
- → See `samber/cc-skills-golang@golang-error-handling` skill for nil error interface trap
- → See `samber/cc-skills-golang@golang-security` skill for security-relevant safety issues (memory safety, integer overflow)
- → See `samber/cc-skills-golang@golang-troubleshooting` skill for debugging panics and race conditions
- → See `samber/cc-skills-golang@golang-continuous-integration` skill for automated AI-driven code review in CI using these guidelines

© samber · MIT · samber/cc-skills-golang

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor