{"record":{"id":"d96754feff71aa4e","repo":"PHPOffice/PhpSpreadsheet","slug":"you-must-specify-a-worksheet-or-a-scope-for-a-name","errorCode":null,"errorMessage":"You must specify a worksheet or a scope for a Named Range","messagePattern":"You must specify a worksheet or a scope for a Named Range","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/NamedRange.php","lineNumber":21,"sourceCode":"namespace PhpOffice\\PhpSpreadsheet;\n\nuse PhpOffice\\PhpSpreadsheet\\Cell\\Coordinate;\nuse PhpOffice\\PhpSpreadsheet\\Worksheet\\Worksheet;\n\nclass NamedRange extends DefinedName\n{\n    /**\n     * Create a new Named Range.\n     */\n    public function __construct(\n        string $name,\n        ?Worksheet $worksheet = null,\n        string $range = 'A1',\n        bool $localOnly = false,\n        ?Worksheet $scope = null\n    ) {\n        if ($worksheet === null && $scope === null) {\n            throw new Exception('You must specify a worksheet or a scope for a Named Range');\n        }\n        parent::__construct($name, $worksheet, $range, $localOnly, $scope);\n    }\n\n    /**\n     * Get the range value.\n     */\n    public function getRange(): string\n    {\n        return $this->value;\n    }\n\n    /**\n     * Set the range value.\n     */\n    public function setRange(string $range): self\n    {\n        if (!empty($range)) {","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/NamedRange.php#L3-L39","documentation":"A NamedRange anchors a defined name to a worksheet, either as its worksheet or as its local scope. The constructor requires at least one of those two; both null throws. Even workbook-global names need a worksheet passed here (scope/locality is then controlled by localOnly/scope).","triggerScenarios":"new NamedRange('Total') with no worksheet or scope; passing a null sheet obtained from $spreadsheet->getSheetByName('Q1') when the sheet title does not match (whitespace, escaping, or case issues); building many ranges in a loop where the sheet variable is occasionally null.","commonSituations":"Assuming a workbook-global name needs no worksheet; sheet lookups returning null because the sheet was renamed or the title contains quotes/apostrophes that were not matched exactly.","solutions":["Pass a worksheet, e.g. new NamedRange('Total', $spreadsheet->getActiveSheet())","For sheet-local names pass the scope worksheet (and localOnly=true), which also satisfies the check","Guard null results from getSheetByName()/getSheet() before constructing, and fail with a clearer message about the missing sheet"],"exampleFix":"// before\n$sheet = $spreadsheet->getSheetByName('Q1 '); // trailing space -> null\n$spreadsheet->addNamedRange(new NamedRange('Total', $sheet)); // throws\n\n// after\n$sheet = $spreadsheet->getSheetByName(trim($name)) ?? $spreadsheet->getActiveSheet();\n$spreadsheet->addNamedRange(new NamedRange('Total', $sheet));","handlingStrategy":"validation","validationCode":"$sheet = $worksheet ?? $scope ?? $spreadsheet->getActiveSheet();\n$spreadsheet->addNamedRange(new NamedRange($name, $sheet, $range, $localOnly));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never rely on getSheetByName() succeeding — null-coalesce to a fallback sheet or fail with your own message","Remember even workbook-global named ranges need a worksheet argument in this API","Centralize named-object creation in one factory so the worksheet/scope rule is enforced once"],"tags":["named-range","defined-name","worksheet-scope","constructor"],"backgroundTag":"missing-required-constructor-argument","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}