{"record":{"id":"814e0a5d55c04774","repo":"PHPOffice/PHPWord","slug":"can-not-delete-row-s-template-variable-not-found-or-variable","errorCode":null,"errorMessage":"Can not delete row %s, template variable not found or variable contains markup.","messagePattern":"Can not delete row (.+?), template variable not found or variable contains markup\\.","errorType":"exception","errorClass":"PhpOffice\\PhpWord\\Exception\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpWord/TemplateProcessor.php","lineNumber":819,"sourceCode":"        $result = $this->getSlice(0, $rowStart);\n        $result .= implode('', $this->indexClonedVariables($numberOfClones, $xmlRow));\n        $result .= $this->getSlice($rowEnd);\n\n        $this->tempDocumentMainPart = $result;\n    }\n\n    /**\n     * Delete a table row in a template document.\n     */\n    public function deleteRow(string $search): void\n    {\n        if (self::$macroOpeningChars !== substr($search, 0, 2) && self::$macroClosingChars !== substr($search, -1)) {\n            $search = self::$macroOpeningChars . $search . self::$macroClosingChars;\n        }\n\n        $tagPos = strpos($this->tempDocumentMainPart, $search);\n        if (!$tagPos) {\n            throw new Exception(sprintf('Can not delete row %s, template variable not found or variable contains markup.', $search));\n        }\n\n        $tableStart = $this->findTableStart($tagPos);\n        $tableEnd = $this->findTableEnd($tagPos);\n        $xmlTable = $this->getSlice($tableStart, $tableEnd);\n\n        if (substr_count($xmlTable, '<w:tr') === 1) {\n            $this->tempDocumentMainPart = $this->getSlice(0, $tableStart) . $this->getSlice($tableEnd);\n\n            return;\n        }\n\n        $rowStart = $this->findRowStart($tagPos);\n        $rowEnd = $this->findRowEnd($tagPos);\n        $xmlRow = $this->getSlice($rowStart, $rowEnd);\n\n        $this->tempDocumentMainPart = $this->getSlice(0, $rowStart) . $this->getSlice($rowEnd);\n","sourceCodeStart":801,"sourceCodeEnd":837,"githubUrl":"https://github.com/PHPOffice/PHPWord/blob/aef95c04151b5633cc505f672ddd6a71da900ee1/src/PhpWord/TemplateProcessor.php#L801-L837","documentation":"TemplateProcessor::deleteRow() searches the document's main XML part for the row placeholder template variable. If the placeholder cannot be found at all, or is found but wrapped in split XML runs (markup), the table boundaries cannot be located and this exception is thrown. The row will not be deleted.","triggerScenarios":"Calling $templateProcessor->deleteRow('placeholder') where the placeholder text does not exist in the document, or where Word split the placeholder across multiple <w:r> runs (e.g. spell-check proofErr tags, formatting changes mid-word, autocorrect).","commonSituations":"Typo in the variable name vs. the docx template; editing the template in Word causing the variable to be split with markup; using deleteRow on a variable that is not inside a table row; template saved by a different tool that encodes characters differently (e.g. smart quotes).","solutions":["Verify the placeholder exists exactly once in the table row of the docx (unzip and grep document.xml).","Retype the placeholder in Word as plain, contiguous text with uniform formatting so it stays in one <w:t> run.","Preprocess document.xml to merge split runs before processing.","Check that the placeholder is not already wrapped in ${...} when passed (deleteRow adds macro chars itself unless they are present)."],"exampleFix":"// before\n$processor->deleteRow('CustomerRow'); // variable split by Word markup -> throws\n// after\n// In document.xml ensure the row contains a single run:\n// <w:r><w:t>CustomerRow</w:t></w:r>\n$processor->deleteRow('CustomerRow');","handlingStrategy":"validation","validationCode":"function placeholderIsPlainText(string $templatePath, string $var): bool {\n    $zip = new ZipArchive();\n    $zip->open($templatePath);\n    $xml = $zip->getFromName('word/document.xml');\n    $zip->close();\n    return preg_match('/<w:t[^>]*>\\s*\\$\\{' . preg_quote($var, '/') . '\\}\\s*<\\/w:t>/', $xml) === 1;\n}","typeGuard":"function isDeleteRowSafe(string $xml, string $var): bool {\n    return preg_match('/<w:tbl>(?:(?!<\\/w:tbl>).)*?<w:t[^>]*>\\s*\\$\\{' . preg_quote($var, '/') . '\\}\\s*<\\/w:t>/s', $xml) === 1;\n}","tryCatchPattern":"try {\n    $processor->deleteRow($var);\n} catch (\\PhpOffice\\PhpWord\\Exception\\Exception $e) {\n    if (str_contains($e->getMessage(), 'Can not delete row')) {\n        // log and continue with the row intact, or fix the template\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Keep template variables as single contiguous text runs (retype them after any Word edit).","Disable spell-check marks / proofErr in templates or clean the XML before processing.","Only call deleteRow on variables that live inside table rows.","Add a pre-flight check that greps document.xml for each variable before processing."],"tags":["phpword","template-processing","docx","xml"],"backgroundTag":"resource-not-found","analyzedSha":"aef95c04151b5633cc505f672ddd6a71da900ee1","analyzedAt":"2026-09-14T10:53:58.933Z","contentChangedAt":"2026-09-14T10:53:58.933Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}