tryCatch の error ハンドラは warning を捕まえない
tryCatch(expr, error = function(e) ...)のerrorハンドラは、条件クラスがerrorであるものしか捕まえません。warning()で発生させた警告は、実行が止まらないままerrorハンドラをすり抜けます。
なぜエラーが出ないのか
出力: result: NaN
(エラーなし)- Rは何も報告しません。文法としても実行としても正しいコードだからです
result: NaN- 実際の出力 — 期待した結果と食い違っている箇所
このエラーが出る典型パターン
パターン1
1 safe_sqrt <- function(x) { 2 tryCatch({ 3 if (x < 0) warning("negative input") 4 sqrt(x) 5 }, error = function(e) { 6 NA 7 } ) ^ 8 } 9 result <- safe_sqrt(-4) 10 cat("result:", result, "\n")
errorハンドラだけを用意してもwarning()による警告は捕まえられず、sqrt(x)がそのまま実行されてNaNになります。捕まえたい場合はwarning=という別のハンドラが必要です。
直し方: (空) を , warning = function(w) NA にします。
パターン2
1 safe_log <- function(x) { 2 tryCatch({ 3 if (x <= 0) warning("non-positive input") 4 log(x) 5 }, error = function(e) { 6 NA 7 } ) ^ 8 } 9 result <- safe_log(-1) 10 cat("result:", result, "\n")
warning()で発生させた警告はerrorハンドラをすり抜けてしまい、log(x)の計算が続行されNaNが返ります。
直し方: (空) を , warning = function(w) NA にします。
パターン3
1 parse_num <- function(s) { 2 tryCatch({ 3 n <- as.numeric(s) 4 n 5 }, error = function(e) { 6 -1 7 } ) ^ 8 } 9 result <- parse_num("abc") 10 cat("result:", result, "\n")
as.numeric("abc")は変換できない警告を出しつつNAを返しますが、この警告はerrorハンドラでは捕まえられません。
直し方: (空) を , warning = function(w) -1 にします。
パターン4
1 safe_div_check <- function(a, b) { 2 tryCatch({ 3 if (b == 0) warning("division by zero") 4 a / b 5 }, error = function(e) { 6 NA 7 } ) ^ 8 } 9 result <- safe_div_check(10, 0) 10 cat("result:", result, "\n")
0除算はwarning()で警告を出すだけで実行は止まらず、errorハンドラには捕まらないままInfという結果になります。
直し方: (空) を , warning = function(w) NA にします。
パターン5
1 parse_int <- function(s) { 2 tryCatch({ 3 n <- as.integer(s) 4 n 5 }, error = function(e) { 6 -1 7 } ) ^ 8 } 9 result <- parse_int("xyz") 10 cat("result:", result, "\n")
as.integer("xyz")が出す「NAs introduced by coercion」という警告はerrorハンドラでは捕まえられず、そのままNAが結果になります。
直し方: (空) を , warning = function(w) 0 にします。
よくある誤解
「tryCatchのerrorハンドラは、異常っぽいものは何でも捕まえてくれるはず」という思い込みは誤りです。R条件システムはerrorとwarningを別の条件クラスとして扱うため、警告も捕まえたい場合はwarning=という別のハンドラを明示的に用意する必要があります。
実務での勘所
Rの条件システムは、conditionという頂点の下にerror・warning・messageが兄弟同士として並ぶ、独立したクラス階層になっています。JavaやRubyの例外階層のように、警告が例外の一種として派生しているわけではありません。error用のハンドラがwarningを拾えないのは、片方がもう片方に含まれる関係ではなく、そもそも別系統の条件だからです。あらゆる種類の条件を1つのハンドラで受け止めたい場合は、tryCatch(expr, condition = function(c) ...)のように、階層の頂点にあるcondition自体を指定する必要があります。