MeWrite Docs

panic: runtime error: index out of range / nil pointer dereference

Goでランタイムパニックが発生した場合の対処法

概要

Goの panic はプログラムの実行を停止させる深刻なエラーです。配列の範囲外アクセスやnilポインタの参照解除などで発生します。

エラーメッセージ

panic: runtime error: index out of range [5] with length 3

goroutine 1 [running]:
main.main()
    /app/main.go:10 +0x45
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x...]

よくあるpanic

1. index out of range

1
2
3
4
5
6
7
8
// NG: インデックスが範囲外
slice := []int{1, 2, 3}
fmt.Println(slice[5])  // panic

// OK: 範囲チェック
if len(slice) > 5 {
    fmt.Println(slice[5])
}

2. nil pointer dereference

1
2
3
4
5
6
7
8
// NG: nilの参照解除
var user *User
fmt.Println(user.Name)  // panic

// OK: nilチェック
if user != nil {
    fmt.Println(user.Name)
}

3. nil map への書き込み

1
2
3
4
5
6
7
// NG: 初期化なしでmapに書き込み
var m map[string]int
m["key"] = 1  // panic

// OK: makeで初期化
m := make(map[string]int)
m["key"] = 1

4. nil channel

1
2
3
4
5
6
7
// NG: nilチャネルへの送受信(ブロックではなくpanicになるケースも)
var ch chan int
close(ch)  // panic

// OK: 初期化
ch := make(chan int)
close(ch)

5. 閉じたchannelへの送信

1
2
3
4
5
6
7
// NG: クローズ済みチャネルへの送信
ch := make(chan int)
close(ch)
ch <- 1  // panic

// OK: 送信前にチェック
// ただし、selectを使うのがベター

解決策

1. インデックスアクセスの安全化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 範囲チェック付きのヘルパー
func safeGet[T any](slice []T, index int) (T, bool) {
    if index >= 0 && index < len(slice) {
        return slice[index], true
    }
    var zero T
    return zero, false
}

// 使用例
if val, ok := safeGet(slice, 5); ok {
    fmt.Println(val)
}

2. nilチェック

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 複数のnilチェック
if user != nil && user.Address != nil {
    fmt.Println(user.Address.City)
}

// ゲッターメソッドでnilを安全に扱う
func (u *User) GetAddress() *Address {
    if u == nil {
        return nil
    }
    return u.Address
}

func (a *Address) GetCity() string {
    if a == nil {
        return ""
    }
    return a.City
}

// 使用
city := user.GetAddress().GetCity()

3. recoverでpanicを捕捉

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func safeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()

    // panicする可能性のある処理
    riskyOperation()
    return nil
}

4. mapの安全なアクセス

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 読み取りはpanicしない
m := map[string]int{"a": 1}
val := m["nonexistent"]  // 0(ゼロ値)が返る

// 存在チェック
if val, ok := m["key"]; ok {
    fmt.Println(val)
}

// 書き込み前に初期化チェック
func safeSet(m *map[string]int, key string, val int) {
    if *m == nil {
        *m = make(map[string]int)
    }
    (*m)[key] = val
}

5. インターフェースのnilチェック

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// インターフェースのnilは注意が必要
type MyInterface interface {
    DoSomething()
}

var i MyInterface
// i == nil は true

var p *MyStruct = nil
i = p
// i == nil は false!(型情報があるため)

// 正しいチェック
if i == nil || reflect.ValueOf(i).IsNil() {
    fmt.Println("nil")
}

よくあるパターン

HTTPハンドラ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func handler(w http.ResponseWriter, r *http.Request) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("panic in handler: %v", r)
            http.Error(w, "Internal Server Error", 500)
        }
    }()

    // ハンドラ処理
}

JSONパース

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
type Response struct {
    Data *Data `json:"data"`
}

type Data struct {
    Items []Item `json:"items"`
}

// NG: nilチェックなし
var resp Response
json.Unmarshal(data, &resp)
fmt.Println(resp.Data.Items[0])  // panic

// OK: nilチェック
var resp Response
if err := json.Unmarshal(data, &resp); err == nil {
    if resp.Data != nil && len(resp.Data.Items) > 0 {
        fmt.Println(resp.Data.Items[0])
    }
}

goroutine内のpanic

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// NG: goroutine内のpanicは親に伝播しない
go func() {
    panic("oops")  // プログラム全体がクラッシュ
}()

// OK: goroutine内でrecover
go func() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("goroutine panic: %v", r)
        }
    }()
    riskyOperation()
}()

デバッグ

1
2
3
4
5
6
7
8
9
// スタックトレースを表示
import "runtime/debug"

defer func() {
    if r := recover(); r != nil {
        fmt.Printf("panic: %v\n", r)
        debug.PrintStack()
    }
}()
1
2
3
4
5
# レースコンディションの検出
go run -race main.go

# デバッグビルド
go build -gcflags="all=-N -l" main.go

関連エラー

関連エラー

Go の他のエラー

最終更新: 2025-12-23