PHPOffice/PHPWord · error · PhpOffice\PhpWord\Exception\Exception
Can not find the start position of the row to clone.
Error message
Can not find the start position of the row to clone.
What it means
findRowStart() searches backwards from the placeholder offset for the opening row tag '<w:tr ' or '<w:tr>'. If neither is found it throws. cloneRow()/deleteRow() need the full row element boundaries to duplicate/remove it, so a missing row start means the placeholder is not in a table row.
Solutions
- Ensure the placeholder is the content of a cell inside a normal <w:tr> row.
- Add a dedicated template row containing just the variable for cloning.
- Validate by unzipping the docx and checking document.xml for '<w:tr' before the variable.
- Fall back to setValue or manual XML manipulation if the row structure is nonstandard.
Example fix
// before
$processor->cloneRow('headerVar', 3); // headerVar sits in the table header -> throws
// after
$processor->cloneRow('rowVar', 3); // rowVar is inside a body row <w:tr><w:tc><w:t>rowVar</w:t>... Defensive patterns
Strategy: validation
Validate before calling
function rowVariableExists(string $templatePath, string $var): bool {
$zip = new ZipArchive();
$zip->open($templatePath);
$xml = $zip->getFromName('word/document.xml');
$zip->close();
$pos = strpos($xml, '${' . $var . '}');
if ($pos === false) return false;
$before = substr($xml, 0, $pos);
return strrpos($before, '<w:tr ') !== false || strrpos($before, '<w:tr>') !== false;
} Try / catch
try {
$processor->cloneRow($var, $count);
} catch (\PhpOffice\PhpWord\Exception\Exception $e) {
if (str_contains($e->getMessage(), 'start position of the row')) {
error_log("Variable $var is not inside a table row");
} else { throw $e; }
} Prevention
- Keep clone target variables in dedicated body rows of tables, not headers/captions.
- Lint templates in CI: assert each row variable has a <w:tr before it.
- Keep the variable as plain single-run text within one cell.
- Avoid hand-editing template XML in ways that remove row markup.
When it happens
Trigger: cloneRow() or deleteRow() called with a variable that is not inside a <w:tr> element — e.g. it sits in the table header/caption, outside any table, or the row markup was removed by hand-editing the template.
Common situations: Variable placed in a table caption or header rather than a body row; cloneRow used on a body-level variable; hand-edited template XML where row markup was removed.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Can not delete row , template variable not found or…
- Can not find the start position of the table.
- The part " " doesn't exist
- Could not load the given XML document.
- Invalid value, on of ' . implode(', ', $position) . '…
AI-assisted analysis of PHPOffice/PHPWord@aef95c0415 (2026-09-14).
Data as JSON: /api/errors/05e501866b1948ad.
Report an issue: GitHub.
Appendix: source
Thrown at src/PhpWord/TemplateProcessor.php:1253
return strpos($this->tempDocumentMainPart, '</w:tbl>', $offset) + 7;
}
/**
* Find the start position of the nearest table row before $offset.
*
* @param int $offset
*
* @return int
*/
protected function findRowStart($offset)
{
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tr ', ((strlen($this->tempDocumentMainPart) - $offset) * -1));
if (!$rowStart) {
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tr>', ((strlen($this->tempDocumentMainPart) - $offset) * -1));
}
if (!$rowStart) {
throw new Exception('Can not find the start position of the row to clone.');
}
return $rowStart;
}
/**
* Find the end position of the nearest table row after $offset.
*
* @param int $offset
*
* @return int
*/
protected function findRowEnd($offset)
{
return strpos($this->tempDocumentMainPart, '</w:tr>', $offset) + 7;
}
/**View on GitHub (pinned to aef95c0415)