{"record":{"id":"6b1ca7fc1b4774a8","repo":"PHPOffice/PhpSpreadsheet","slug":"cell-range-range-not-known-as-protected","errorCode":null,"errorMessage":"Cell range {$range} not known as protected.","messagePattern":"Cell range (.+?) not known as protected\\.","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Worksheet/Worksheet.php","lineNumber":2046,"sourceCode":"    }\n\n    /**\n     * Remove protection on a cell or cell range.\n     *\n     * @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'\n     *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),\n     *              or a CellAddress or AddressRange object.\n     *\n     * @return $this\n     */\n    public function unprotectCells(AddressRange|CellAddress|int|string|array $range): static\n    {\n        $range = Functions::trimSheetFromCellReference(Validations::validateCellOrCellRange($range));\n\n        if (isset($this->protectedCells[$range])) {\n            unset($this->protectedCells[$range]);\n        } else {\n            throw new Exception('Cell range ' . $range . ' not known as protected.');\n        }\n\n        return $this;\n    }\n\n    /**\n     * Get protected cells.\n     *\n     * @return ProtectedRange[]\n     */\n    public function getProtectedCellRanges(): array\n    {\n        return $this->protectedCells;\n    }\n\n    /**\n     * Get Autofilter.\n     */","sourceCodeStart":2028,"sourceCodeEnd":2064,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Worksheet/Worksheet.php#L2028-L2064","documentation":"Worksheet::unprotectCells() checks the exact normalized range string against the sheet's protectedCells map and throws when it is not a key. The input goes through trimSheetFromCellReference + validateCellOrCellRange (which expand a single cell to 'A1:A1'), but the resulting string must still equal the exact range used when protectCells() registered it.","triggerScenarios":"unprotectCells('A1:B10') when protection was registered as 'A1:B5' or never at all; unprotecting a differently formatted string (e.g. with '$' signs or lowercase) that does not match the stored key; calling unprotect after a fresh load where the protection lives on another sheet.","commonSituations":"Tracking protection ranges in your own DB/schema whose formatting drifts from what protectCells() stored; unprotecting guessed boundaries; sheet-level protection vs cell-range protection being confused.","solutions":["Enumerate the real entries via $sheet->getProtectedCellRanges() (returns ProtectedRange objects — use their range string, e.g. $pr->getRange()) and unprotect exactly that","Guard before the call: in_array($range, array_keys-ish list, true) or inspect getProtectedCellRanges() names/ranges","If nothing is protected, skip the call rather than unprotecting speculatively"],"exampleFix":"// before\n$sheet->unprotectCells('A1:B10'); // actual protected range was 'A1:B5' -> throws\n\n// after\nforeach ($sheet->getProtectedCellRanges() as $protectedRange) {\n    $sheet->unprotectCells($protectedRange->getRange()); // exact stored string\n}","handlingStrategy":"validation","validationCode":"$known = array_map(\n    fn (ProtectedRange $pr) => $pr->getRange(),\n    $sheet->getProtectedCellRanges()\n);\nif (in_array($range, $known, true)) {\n    $sheet->unprotectCells($range);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $sheet->unprotectCells($range);\n} catch (\\PhpOffice\\PhpSpreadsheet\\Exception $e) {\n    if (str_contains($e->getMessage(), 'not known as protected')) {\n        // nothing was protected under that exact range — skip\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Store the exact range string you passed to protectCells() if you must undo it later","Source unprotect arguments from getProtectedCellRanges(), not from external bookkeeping","Don't speculatively unprotect; check the protected set first"],"tags":["phpspreadsheet","protection","cell-range","state-mismatch","exact-match"],"backgroundTag":"invalid-state-transition","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}