From 6ec121d8c6cbe16eee847440fd1be15eeea6e195 Mon Sep 17 00:00:00 2001 From: Adam Altmejd Date: Thu, 19 Mar 2026 11:16:13 +0100 Subject: [PATCH] fix: guard IV first-stage SSR checks against NaN from non-convergence When the demeaning algorithm does not converge (e.g. with varying slopes and weights), SSR can be NaN. The bare comparisons `my_res$ssr < 1e-10` then produce NA, causing `if (error_endo_no_variation || error_inst_no_expl)` to error with "missing value where TRUE/FALSE needed". Wrapping in isTRUE() lets execution continue so the existing convergence warning surfaces, consistent with the non-IV code path. Co-Authored-By: Claude Opus 4.6 (1M context) --- R/estimation.R | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/R/estimation.R b/R/estimation.R index be0a40f3..9eade05f 100644 --- a/R/estimation.R +++ b/R/estimation.R @@ -1349,32 +1349,33 @@ feols = function(fml, data, vcov, weights, offset, subset, split, fsplit, split. res_first_stage[[iv_lhs_names[i]]] = my_res # Checking errors - error_endo_no_variation = my_res$ssr < 1e-10 - error_inst_no_expl = abs(my_res$ssr - my_res$ssr_no_inst) < 1e-10 + # NaN ssr arises from demeaning non-convergence; isTRUE guards against NA in the if() + error_endo_no_variation = isTRUE(my_res$ssr < 1e-10) + error_inst_no_expl = isTRUE(abs(my_res$ssr - my_res$ssr_no_inst) < 1e-10) if(error_endo_no_variation || error_inst_no_expl){ # we keep the information on this first stage => useful for reporting - + if(error_endo_no_variation){ msg = sma("[IV error] The endogenous variable {bq ? iv_lhs_names[i]} is ", "fully explained by the exogenous variables and the instruments. ", "Please revise the model.") - + } else if(error_inst_no_expl){ - msg = sma("[IV error] The instruments have 0 explanatory power ", + msg = sma("[IV error] The instruments have 0 explanatory power ", "for the endogenous variable {bq ? iv_lhs_names[i]}. ", "Please revise the model.") } - - + + if(IN_MULTI){ stack_multi_notes(msg) return(fixest_NA_results_IV(env, res_first_stage, msg)) - + } else { mema("[IV error] Problematic 1st stage results:") my_res$is_iv = FALSE print(my_res) - + stopi("{msg}\nFor information, above are reported the results of the 1st stage estimation.") } @@ -1513,33 +1514,34 @@ feols = function(fml, data, vcov, weights, offset, subset, split, fsplit, split. res_first_stage[[iv_lhs_names[i]]] = my_res # Checking errors - error_endo_no_variation = my_res$ssr < 1e-10 - error_inst_no_expl = abs(my_res$ssr - my_res$ssr_no_inst) < 1e-10 + # NaN ssr arises from demeaning non-convergence; isTRUE guards against NA in the if() + error_endo_no_variation = isTRUE(my_res$ssr < 1e-10) + error_inst_no_expl = isTRUE(abs(my_res$ssr - my_res$ssr_no_inst) < 1e-10) if(error_endo_no_variation || error_inst_no_expl){ # we keep the information on this first stage => useful for reporting - + if(error_endo_no_variation){ msg = sma("[IV error] The endogenous variable {bq ? iv_lhs_names[i]} is ", "fully explained by the exogenous variables and the instruments. ", "Please revise the model.") - + } else if(error_inst_no_expl){ - msg = sma("[IV error] The instruments have 0 explanatory power ", + msg = sma("[IV error] The instruments have 0 explanatory power ", "for the endogenous variable {bq ? iv_lhs_names[i]}. ", "Please revise the model.") } - - + + if(IN_MULTI){ stack_multi_notes(msg) return(fixest_NA_results_IV(env, res_first_stage, msg)) - + } else { - + mema("[IV error] Problematic 1st stage results:") my_res$is_iv = FALSE print(my_res) - + stopi("{msg}\nFor information, above are reported the results of the 1st stage estimation.") }