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.') # }