From 62fde43ce6e736dd8ac045b17a0e75d8cf215844 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:30:47 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20Fix=20unhan?= =?UTF-8?q?dled=20state=20exceptions=20in=20deterministic=20model=20failur?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mirt 모델 추정 실패 시 객체가 생성되지 않은 상태에서 속성을 접근하려고 하여 발생하는 런타임 예외와 잠재적인 내부 상태 누출 문제를 해결했습니다. try() 블록 내부의 결정적 오류(deterministic errors)로 인해 객체 생성이 실패한 경우를 대비하여 isTRUE()와 exists()를 활용한 짧은 순환(short-circuit) 평가를 통해 객체의 존재 여부를 먼저 확인하도록 코드를 개선했습니다. --- .jules/sentinel.md | 7 +++++++ R/aFIPC.R | 34 +++++++++++++++++----------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 75b4a40..7539db6 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -17,3 +17,10 @@ 1. Always replace `while (!exists(...))` retries with a bounded `for` loop (e.g., `for (attempt in seq_len(3))`). 2. Include an explicit check for the success condition inside the loop (`if (exists('model')) break`). 3. After the loop, verify success and fail securely with an explicit error (`if (!exists('model')) stop(...)`) to prevent unhandled exceptions downstream. + +## 2024-06-25 - Unhandled State Error During Deterministic Model Failures +**Vulnerability:** When checking convergence or attributes on model objects (e.g. `!oldFormModel@OptimInfo$secondordertest`), if the object estimation inside a `try()` block failed, the object was never instantiated. This led to "object not found" runtime exceptions and potential internal state leakage in upstream evaluation pipelines if unhandled completely. +**Learning:** Checking properties of an object instantiated within a deterministic error block (like `try()`) requires ensuring the object itself actually exists before referencing its attributes. Failing to do so causes subsequent execution steps to fail ungracefully. +**Prevention:** +1. Always use `exists('object')` coupled with `isTRUE(object@property)` checks rather than directly referencing properties that might evaluate to a boolean check on a non-existent object. +2. In R scripts, explicitly prepend existence validations like `(!exists('oldFormModel') || !isTRUE(oldFormModel@OptimInfo$secondordertest))` before conditionally handling retries or throwing fallback errors. diff --git a/R/aFIPC.R b/R/aFIPC.R index 2e5ac4a..c0b1bfa 100644 --- a/R/aFIPC.R +++ b/R/aFIPC.R @@ -184,7 +184,7 @@ autoFIPC <- if (tryFitwholeOldItems == T) { if ( - !oldFormModel@OptimInfo$secondordertest && + (!exists('oldFormModel') || !isTRUE(oldFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -208,7 +208,7 @@ autoFIPC <- } if ( - !oldFormModel@OptimInfo$secondordertest && + (!exists('oldFormModel') || !isTRUE(oldFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -237,7 +237,7 @@ autoFIPC <- } if ( - !oldFormModel@OptimInfo$secondordertest && + (!exists('oldFormModel') || !isTRUE(oldFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -255,7 +255,7 @@ autoFIPC <- } if ( - !oldFormModel@OptimInfo$secondordertest && + (!exists('oldFormModel') || !isTRUE(oldFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -274,7 +274,7 @@ autoFIPC <- } if ( - !oldFormModel@OptimInfo$secondordertest && + (!exists('oldFormModel') || !isTRUE(oldFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -293,7 +293,7 @@ autoFIPC <- } if ( - !oldFormModel@OptimInfo$secondordertest && + (!exists('oldFormModel') || !isTRUE(oldFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -312,7 +312,7 @@ autoFIPC <- } if ( - !oldFormModel@OptimInfo$secondordertest && + (!exists('oldFormModel') || !isTRUE(oldFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { stop('Estimation failed. Please check test quality.') @@ -398,7 +398,7 @@ autoFIPC <- if (tryFitwholeNewItems) { if ( - !newFormModel@OptimInfo$secondordertest && + (!exists('newFormModel') || !isTRUE(newFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -422,7 +422,7 @@ autoFIPC <- } if ( - !newFormModel@OptimInfo$secondordertest && + (!exists('newFormModel') || !isTRUE(newFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -451,7 +451,7 @@ autoFIPC <- } if ( - !newFormModel@OptimInfo$secondordertest && + (!exists('newFormModel') || !isTRUE(newFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -469,7 +469,7 @@ autoFIPC <- } if ( - !newFormModel@OptimInfo$secondordertest && + (!exists('newFormModel') || !isTRUE(newFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -488,7 +488,7 @@ autoFIPC <- } if ( - !newFormModel@OptimInfo$secondordertest && + (!exists('newFormModel') || !isTRUE(newFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -507,7 +507,7 @@ autoFIPC <- } if ( - !newFormModel@OptimInfo$secondordertest && + (!exists('newFormModel') || !isTRUE(newFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { message( @@ -526,7 +526,7 @@ autoFIPC <- } if ( - !newFormModel@OptimInfo$secondordertest && + (!exists('newFormModel') || !isTRUE(newFormModel@OptimInfo$secondordertest)) && !itemtype == 'ideal' ) { stop('Estimation failed. Please check test quality.') @@ -965,14 +965,14 @@ autoFIPC <- } } - # if(!LinkedModel@OptimInfo$secondordertest){ + # if((!exists('LinkedModel') || !isTRUE(LinkedModel@OptimInfo$secondordertest))){ # message('Estimation failed. estimating new parameters with no prior distribution using quasi-Monte Carlo EM estimation. please be patient.') # # rm(LinkedModel) # try(LinkedModel <- mirt::mirt(data = newformXDataK[colnames(newFormModel@Data$data)], LinkedModelSyntax, itemtype = newFormModel@Model$itemtype, SE = T, method = 'QMCEM', accelerate = 'squarem', technical = list(NCYCLES = 1e+5), pars = NewScaleParms, GenRandomPars = F)) # } # - # if(!LinkedModel@OptimInfo$secondordertest){ + # if((!exists('LinkedModel') || !isTRUE(LinkedModel@OptimInfo$secondordertest))){ # message('Estimation failed. estimating new parameters with no prior distribution using Cai\'s (2010) Metropolis-Hastings Robbins-Monro (MHRM) algorithm. please be patient.') # # try(rm(LinkedModel), silent = TRUE) @@ -983,7 +983,7 @@ autoFIPC <- # if (!exists('LinkedModel')) stop('Failed to estimate LinkedModel with MHRM after 3 attempts') # } - # if(!LinkedModel@OptimInfo$secondordertest){ + # if((!exists('LinkedModel') || !isTRUE(LinkedModel@OptimInfo$secondordertest))){ # stop('Estimation failed. Please check test quality.') # }