getgrav/grav · error · InvalidArgumentException
Invalid arguments, expected DOMElement or DOMDocument
Error message
Invalid arguments, expected DOMElement or DOMDocument
What it means
DOMLettersIterator walks an HTML/DOM subtree one character at a time (used by Grav\Common\Helpers\Truncator::truncateLetters). Its constructor only accepts a DOMElement, or a DOMDocument from which it takes documentElement. If you hand it any other DOMNode subtype — DOMText, DOMAttr, DOMComment — or a DOMDocument that has no documentElement (empty/failed load), the instanceof check fails and it throws InvalidArgumentException. The iterator needs a real element as its recursion root, so it refuses anything else.
Source
Thrown at system/src/DOMLettersIterator.php:47
private $offset = -1;
/** @var int|null */
private $key;
/** @var array<int,string>|null */
private $letters;
/**
* expects DOMElement or DOMDocument (see DOMDocument::load and DOMDocument::loadHTML)
*
* @param DOMNode $el
*/
public function __construct(DOMNode $el)
{
if ($el instanceof DOMDocument) {
$el = $el->documentElement;
}
if (!$el instanceof DOMElement) {
throw new InvalidArgumentException('Invalid arguments, expected DOMElement or DOMDocument');
}
$this->start = $el;
}
/**
* Returns position in text as DOMText node and character offset.
* (it's NOT a byte offset, you must use mb_substr() or similar to use this offset properly).
* node may be NULL if iterator has finished.
*
* @return array
*/
public function currentTextPosition(): array
{
return [$this->current, $this->offset];
}
/**View on GitHub (pinned to 6040efed04)
Solutions
- Pass the element itself: for a DOMDocument use $doc->documentElement, for a fragment use the wrapper DOMElement (Truncator uses the <div> it injected via loadHTML("<div>$html</div>"))
- Null-check any node fetched by ->item(0)/->firstChild before constructing the iterator
- If starting from raw HTML, wrap and load it first: $doc = new DOMDocument(); $doc->loadHTML('<div>' . $html . '</div>'); then pass $doc or its documentElement
- Add an instanceof DOMElement/DOMDocument guard at your call site so the failure surfaces with your own context
Example fix
// before
$iterator = new DOMLettersIterator($node); // $node may be DOMText or null
// after
if ($node instanceof DOMDocument) {
$node = $node->documentElement;
}
if (!$node instanceof DOMElement) {
throw new InvalidArgumentException('Expected DOMElement, got ' . get_class($node));
}
$iterator = new DOMLettersIterator($node); Defensive patterns
Strategy: type-guard
Validate before calling
if ($node instanceof DOMDocument) {
$node = $node->documentElement;
}
if (!$node instanceof DOMElement) {
throw new InvalidArgumentException('DOMLettersIterator needs a DOMElement, got ' . (is_object($node) ? get_class($node) : gettype($node)));
} Type guard
/**
* True when DOMLettersIterator/DOMWordsIterator will accept the node.
* @param DOMNode|DOMDocument|null $node
*/
function isTraversableDomRoot($node): bool
{
if ($node instanceof DOMDocument) {
$node = $node->documentElement;
}
return $node instanceof DOMElement;
} Prevention
- Never pass ->firstChild/->item(0) results unchecked — they are null when nothing matched
- When loading raw HTML, always wrap it: $doc->loadHTML('<div>' . $html . '</div>') so an element root is guaranteed
- Remember an empty DOMDocument has a null documentElement; guard that case explicitly
When it happens
Trigger: Calling new DOMLettersIterator($node) where $node is a DOMText/DOMAttr/DOMComment (e.g. a node grabbed via ->firstChild or ->nodeValue's parent), passing null (e.g. getElementsByTagName('div')->item(0) returned null because the markup had no div), or passing a DOMDocument produced by loadHTML('') whose documentElement is null.
Common situations: Custom Twig filters or plugins that truncate HTML and pass the wrong node from a DOM walk; feeding Truncator-style code malformed HTML so the expected wrapper element is missing; code upgraded from a version that silently tolerated other node types.
Related errors
- Invalid arguments, expected DOMElement or DOMDocument
- Configuration is missing streams.schemes!
- Unable to %s: %s
- Unknown error
- Unable to create directory: %s
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/d04ffbfad8e3ba6a.
Report an issue: GitHub.