Go言語のエラーとpanic
2026/07/28

error型

errorは組み込みインターフェースです。

type error interface {
    Error() string
}

エラーは値

Goでは失敗を例外で飛ばさず、通常は戻り値のerrorとして返します。呼び出し側は毎回if err != nilで分岐し、成功パスと失敗パスを明示的に書きます。

これは制御フローの一部です。エラーを無視するには_で捨てる必要があり、コンパイラは未使用のerrorを警告しませんが、チェックを省略すると失敗が伝播しません。errorを無視して後続の値(nilポインタ、ゼロ値など)を使い続けると、別の場所でパニックやデータ破損につながることがあります。

f, _ := os.Open("missing.txt")
var buf [10]byte
_, _ = f.Read(buf) // 開け失敗時fはnil → panic

呼び出し側は取得直後にerrをチェックします。

f, err := os.Open("file.txt")
if err != nil {
    return err
}
// ここに来た時点でfは開けている

エラーの生成

エラー値はerrors.Newfmt.Errorfで作ります。

固定の文言だけで足りるときはerrors.Newを使います。

return errors.New("not found")

パスや件数など、値をメッセージに埋め込むとき、または下位のerrorに文脈を足して返すときはfmt.Errorfを使います。下位のerrorを包むときは%wを使います。単に文字として並べるだけなら%vです。

return fmt.Errorf("open %s: %w", path, err) // errを包む(呼び出し側で取り出せる)

return fmt.Errorf("open %s: %v", path, err) // errを文字列として埋め込むだけ

return fmt.Errorf("%d items left", n)

呼び出し側では、errors.Isで元のエラーかどうかを調べられます。%wで包んだときだけ一致し、%vではメッセージに文字が入るだけで元のerrorとしては取り出せません。

var ErrNotFound = errors.New("not found")

wrapped := fmt.Errorf("load config: %w", ErrNotFound)
fmt.Println(errors.Is(wrapped, ErrNotFound)) // true

textOnly := fmt.Errorf("load config: %v", ErrNotFound)
fmt.Println(errors.Is(textOnly, ErrNotFound)) // false

nil errorは成功

errorのゼロ値はnilです。err == nilのとき処理は成功したとみなします。非nilのerror値が返ったとき、呼び出し側は失敗として扱います。

func mayFail(ok bool) error {
    if ok {
        return nil // 成功
    }
    return errors.New("failed")
}

if err := mayFail(true); err != nil {
    // 失敗
} else {
    // err == nilなら成功
}

if err != nilの位置

エラーは直後にチェックするのが慣習です。後続のコードが成功を前提に書けるようになります。

panic

panicは実行時に異常を報告し、通常はそのゴルーチンの実行を中断します。

ライブラリの公開APIでは、呼び出し側が予期しうる失敗(ファイルが無い、形式が不正など)はerrorで返します。panicはプログラムのバグ、不変条件の破れ、回復不能な内部状態向けです。予期可能な失敗をpanicにすると、呼び出し側が通常のerrorチェックで扱えず、APIの契約が一貫しなくなります。

if p == nil {
    panic("nil pointer") // バグの検出向け
}

recover

recoverpanicによって中断された実行を再開します。deferされた関数の中でのみ有効です。recoverは最後の防衛線であり、ループや分岐の代わりに使うと読みにくく、想定外のpanicも飲み込んでしまいます。

defer func() {
    if r := recover(); r != nil {
        fmt.Println("recovered:", r)
    }
}()

recoverのスコープ

recoverが効くのは、同じゴルーチン内でpanicしたときだけです。別ゴルーチンで起きたpanicは、そのゴルーチンを終了させ、回復されなければプログラム全体が終了します。呼び出し元のdefer内のrecoverは、別ゴルーチンで起きたpanicを捕まえられません。