{"record":{"id":"362d56b4b27cb970","repo":"PHPOffice/PhpSpreadsheet","slug":"num","errorCode":"#NUM!","errorMessage":"#NUM!","messagePattern":"#NUM!","errorType":"error_code","errorClass":"PhpOffice\\PhpSpreadsheet\\Calculation\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php","lineNumber":106,"sourceCode":"    }\n\n    /**\n     * Convert year from multiple formats to int.\n     */\n    private static function getYear(mixed $year, int $baseYear): int\n    {\n        if ($year === null) {\n            $year = 0;\n        } elseif (is_scalar($year)) {\n            $year = StringHelper::testStringAsNumeric((string) $year);\n        }\n        if (!is_numeric($year)) {\n            throw new Exception(ExcelError::VALUE());\n        }\n        $year = (int) $year;\n\n        if ($year < ($baseYear - 1900)) {\n            throw new Exception(ExcelError::NAN());\n        }\n        if ((($baseYear - 1900) !== 0) && ($year < $baseYear) && ($year >= 1900)) {\n            throw new Exception(ExcelError::NAN());\n        }\n\n        if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {\n            $year += 1900;\n        }\n\n        return (int) $year;\n    }\n\n    /**\n     * Convert month from multiple formats to int.\n     */\n    private static function getMonth(mixed $month): int\n    {\n        if (is_string($month)) {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php#L88-L124","documentation":"RowIterator::seek() validates the row against the iterator's own [startRow, endRow] window, where endRow defaults to the worksheet's highest row captured at construction. Unlike resetStart() it never consults the live sheet, so even a row that now exists can be rejected when it lies beyond the frozen end bound, and any row before startRow is always rejected.","triggerScenarios":"Manual navigation $rowIterator->seek($n) with $n outside [startRow, endRow]; write-back loops calling seek($current + 1) that walk past the end; using an iterator constructed before rows were appended (stale endRow).","commonSituations":"for loops bounded by count($data) while the iterator was built against fewer rows; mixing 0-based counters with 1-based row numbers; reusing iterators across data-refresh cycles.","solutions":["Iterate with foreach instead of seek counters - foreach cannot leave the window","Refresh the upper bound before seeking: construct a fresh $sheet->getRowIterator($start, $sheet->getHighestRow())","Bounds-check your row number against the same start/end values you built the iterator with"],"exampleFix":"// before\n$it = $sheet->getRowIterator(1, 10);\n$it->seek(11); // 11 > 10 -> exception\n\n// after\n$it = $sheet->getRowIterator(1, $sheet->getHighestRow());\nforeach ($it as $row) {\n    // safe navigation\n}","handlingStrategy":"validation","validationCode":"$endRow = $sheet->getHighestRow(); // or the bound you constructed the iterator with\nif ($row < $startRow || $row > $endRow) {\n    // out of the iterator window; extend the range or skip\n}","typeGuard":null,"tryCatchPattern":"try {\n    $rowIterator->seek($row);\n} catch (\\PhpOffice\\PhpSpreadsheet\\Exception $e) {\n    // row outside [startRow, endRow]; rebuild iterator with a fresh bound\n}","preventionTips":["Use foreach instead of manual seek counters - it cannot leave the window","Rebuild iterators after appending rows (endRow is a snapshot)","Keep loop bounds and iterator bounds from the same source variable"],"tags":["iterator","row-iterator","out-of-range","seek"],"backgroundTag":"index-out-of-range","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}