nextcloud/server · error · Exception

{xml_error_string} At line: {line_number}

Error message

{xml_error_string} At line: {line_number}

What it means

XmlImporter (the xcal import path of ImportService) streams the source file through PHP's expat parser in 4096-byte chunks and throws when xml_parse() fails, combining xml_error_string() (e.g. 'Mismatched tag', 'Not well-formed') with the line number reported by xml_get_current_line_number(). It means the supplied xCal/XML is not well-formed at that line.

Source

Thrown at apps/dav/lib/CalDAV/Import/XmlImporter.php:64

	private function analyze() {
		$this->praseLevel = 0;
		$this->prasePath = [];
		$this->componentStart = null;
		$this->componentEnd = null;
		$this->componentLevel = 0;
		$this->componentId = null;
		$this->componentType = null;
		$this->componentIdProperty = false;
		// Create the parser and assign tag handlers
		$parser = xml_parser_create();
		xml_set_object($parser, $this);
		xml_set_element_handler($parser, $this->tagStart(...), $this->tagEnd(...));
		xml_set_default_handler($parser, $this->tagContents(...));
		// iterate through the source data chuck by chunk to trigger the handlers
		@fseek($this->source, 0);
		while ($chunk = fread($this->source, 4096)) {
			if (!xml_parse($parser, $chunk, feof($this->source))) {
				throw new Exception(
					xml_error_string(xml_get_error_code($parser))
					. ' At line: '
					. xml_get_current_line_number($parser)
				);
			}
		}
		//Free up the parser
		xml_parser_free($parser);
	}

	/**
	 * Handles start of tag events from the parser for all tags
	 */
	private function tagStart(XMLParser $parser, string $tag, array $attributes): void {
		// add the tag to the path tracker and increment depth the level
		$this->praseLevel++;
		$this->prasePath[$this->praseLevel] = $tag;
		// determine if the tag is a component type and remember the byte position

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Run xmllint --noout file.xcal (or an equivalent) to get the same line-numbered diagnosis and fix that spot
  2. Re-export or re-transfer the file if it is truncated (compare byte size/checksum with the source)
  3. Encode special characters (&, <) as XML entities and make the encoding declaration match the actual file encoding

Example fix

// before
$raw = "<icalendar><vcalendar><VEVENT><SUMMARY>R&D</SUMMARY>..."; // bare & breaks XML

// after
$raw = "<icalendar><vcalendar><VEVENT><SUMMARY>R&amp;D</SUMMARY>...";
// or escape programmatically before writing:
$summary = htmlspecialchars($summary, ENT_XML1 | ENT_QUOTES, 'UTF-8');
Defensive patterns

Strategy: validation

Validate before calling

$doc = simplexml_load_file($path);
if ($doc === false) {
    foreach (libxml_get_errors() as $err) {
        error_log("XML error line {$err->line}: {$err->message}");
    }
    libxml_clear_errors();
    exit(1);
}

Type guard

function isWellFormedXml(string $xml): bool {
    libxml_use_internal_errors(true);
    $ok = simplexml_load_string($xml) !== false;
    libxml_clear_errors();
    return $ok;
}

Try / catch

try {
    $service->import($source, $calendar, $options)->current();
} catch (\Exception $e) {
    // message carries the expat error string and line number; fix that line and retry
}

Prevention

When it happens

Trigger: Calling ImportService::import() with format 'xcal' on a resource whose XML is malformed: unbalanced tags, invalid entities, encoding declaration mismatch, or a truncated upload.

Common situations: Converting iCal to xCal with a buggy script producing raw '&' characters; files truncated by upload limits or interrupted transfers; UTF-16 files declared as UTF-8; chunk boundaries exposing a truncated final chunk.

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/6965ec3dddb869f8. Report an issue: GitHub.