{"record":{"id":"521e82e37242fa7c","repo":"PHPOffice/PhpSpreadsheet","slug":"reaching-fourth-argument-of-an-if","errorCode":null,"errorMessage":"Reaching fourth argument of an IF","messagePattern":"Reaching fourth argument of an IF","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Calculation\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php","lineNumber":154,"sourceCode":"            $this->braceDepthMap[$this->pendingStoreKey] = 0;\n        } elseif (!empty($this->pendingStoreKey) && array_key_exists($this->pendingStoreKey, $this->braceDepthMap)) {\n            // this is not an if but we go deeper\n            ++$this->braceDepthMap[$this->pendingStoreKey];\n        }\n    }\n\n    public function argumentSeparator(): void\n    {\n        if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === 0) {\n            // We must go to the IF next argument\n            if ($this->conditionMap[$this->pendingStoreKey]) {\n                $this->conditionMap[$this->pendingStoreKey] = false;\n                $this->thenMap[$this->pendingStoreKey] = true;\n            } elseif ($this->thenMap[$this->pendingStoreKey]) {\n                $this->thenMap[$this->pendingStoreKey] = false;\n                $this->elseMap[$this->pendingStoreKey] = true;\n            } elseif ($this->elseMap[$this->pendingStoreKey]) {\n                throw new Exception('Reaching fourth argument of an IF');\n            }\n        }\n    }\n\n    public function closingBrace(mixed $value): void\n    {\n        if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === -1) {\n            // we are closing an IF(\n            if ($value !== 'IF(') {\n                throw new Exception('Parser bug we should be in an \"IF(\"');\n            }\n\n            if ($this->conditionMap[$this->pendingStoreKey]) {\n                throw new Exception('We should not be expecting a condition');\n            }\n\n            $this->thenMap[$this->pendingStoreKey] = false;\n            $this->elseMap[$this->pendingStoreKey] = false;","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php#L136-L172","documentation":"The calculation engine's BranchPruner tracks the three slots of IF(condition, then, else) while parsing. When a fourth argument separator arrives while the pruner is already in the else slot, argumentSeparator() throws 'Reaching fourth argument of an IF', aborting the parse - Excel's IF accepts at most three arguments.","triggerScenarios":"Formulas like =IF(A1>0,\"y\",\"n\",\"x\"); =IF(A1,1,2,) where a trailing comma creates an empty fourth argument; concatenation bugs that append or double a comma inside an IF().","commonSituations":"Programmatically built formula strings with an accidental trailing or doubled comma (,,); user-typed formulas pasted from emails/chat that lost or gained commas; CSV/imported data used to assemble conditionals.","solutions":["Remove the fourth argument - IF takes exactly condition, true-value, and optionally false-value.","Audit generated formulas for trailing commas inside IF(...) before setting cell values: an IF with 3+ top-level commas is malformed.","When calculating cells with user formulas, catch PhpOffice\\PhpSpreadsheet\\Calculation\\Exception and surface the cell coordinate to the user.","If you need extra branches, nest IFs (=IF(a,x,IF(b,y,z))) or use IFS/SWITCH instead of a 4th argument."],"exampleFix":"// before - parser aborts\n$cell->setValue('=IF(A1>0,\"big\",\"small\",)');   // trailing comma = 4th arg\n\n// after\n$cell->setValue('=IF(A1>0,\"big\",\"small\")');","handlingStrategy":"try-catch","validationCode":"// Reject generated IF() formulas that have a 4th argument (including a trailing comma).\nfunction ifHasFourthArgument(string $formula): bool\n{\n    return (bool) preg_match('/=\\s*IF\\([^()]*,[^()]*,[^()]*,/i', $formula);\n}","typeGuard":null,"tryCatchPattern":"use PhpOffice\\PhpSpreadsheet\\Calculation\\Exception as CalcException;\n\ntry {\n    $sheet->getCell('B2')->setValue($userFormula);\n    $value = $sheet->getCell('B2')->calculate();\n} catch (CalcException $e) {\n    if (str_contains($e->getMessage(), 'fourth argument of an IF')) {\n        $errors['B2'] = 'IF() takes at most 3 arguments; check trailing commas';\n    }\n}","preventionTips":["When concatenating IF() formulas, never end the argument list with a comma.","Lint generated formulas for IF( with three or more top-level commas.","Prefer nested IFs, IFS() or SWITCH() for extra branches."],"tags":["phpspreadsheet","formula-parser","if-function","branch-pruner","formula-string"],"backgroundTag":"formula-parser-error","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}