{"record":{"id":"6965ec3dddb869f8","repo":"nextcloud/server","slug":"xml-error-string-at-line-line-number","errorCode":null,"errorMessage":"{xml_error_string} At line: {line_number}","messagePattern":"(.+?) At line: (.+?)","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"apps/dav/lib/CalDAV/Import/XmlImporter.php","lineNumber":64,"sourceCode":"\tprivate function analyze() {\n\t\t$this->praseLevel = 0;\n\t\t$this->prasePath = [];\n\t\t$this->componentStart = null;\n\t\t$this->componentEnd = null;\n\t\t$this->componentLevel = 0;\n\t\t$this->componentId = null;\n\t\t$this->componentType = null;\n\t\t$this->componentIdProperty = false;\n\t\t// Create the parser and assign tag handlers\n\t\t$parser = xml_parser_create();\n\t\txml_set_object($parser, $this);\n\t\txml_set_element_handler($parser, $this->tagStart(...), $this->tagEnd(...));\n\t\txml_set_default_handler($parser, $this->tagContents(...));\n\t\t// iterate through the source data chuck by chunk to trigger the handlers\n\t\t@fseek($this->source, 0);\n\t\twhile ($chunk = fread($this->source, 4096)) {\n\t\t\tif (!xml_parse($parser, $chunk, feof($this->source))) {\n\t\t\t\tthrow new Exception(\n\t\t\t\t\txml_error_string(xml_get_error_code($parser))\n\t\t\t\t\t. ' At line: '\n\t\t\t\t\t. xml_get_current_line_number($parser)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t//Free up the parser\n\t\txml_parser_free($parser);\n\t}\n\n\t/**\n\t * Handles start of tag events from the parser for all tags\n\t */\n\tprivate function tagStart(XMLParser $parser, string $tag, array $attributes): void {\n\t\t// add the tag to the path tracker and increment depth the level\n\t\t$this->praseLevel++;\n\t\t$this->prasePath[$this->praseLevel] = $tag;\n\t\t// determine if the tag is a component type and remember the byte position","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/CalDAV/Import/XmlImporter.php#L46-L82","documentation":"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.","triggerScenarios":"Calling ImportService::import() with format 'xcal' on a resource whose XML is malformed: unbalanced tags, invalid entities, encoding declaration mismatch, or a truncated upload.","commonSituations":"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.","solutions":["Run xmllint --noout file.xcal (or an equivalent) to get the same line-numbered diagnosis and fix that spot","Re-export or re-transfer the file if it is truncated (compare byte size/checksum with the source)","Encode special characters (&, <) as XML entities and make the encoding declaration match the actual file encoding"],"exampleFix":"// before\n$raw = \"<icalendar><vcalendar><VEVENT><SUMMARY>R&D</SUMMARY>...\"; // bare & breaks XML\n\n// after\n$raw = \"<icalendar><vcalendar><VEVENT><SUMMARY>R&amp;D</SUMMARY>...\";\n// or escape programmatically before writing:\n$summary = htmlspecialchars($summary, ENT_XML1 | ENT_QUOTES, 'UTF-8');","handlingStrategy":"validation","validationCode":"$doc = simplexml_load_file($path);\nif ($doc === false) {\n    foreach (libxml_get_errors() as $err) {\n        error_log(\"XML error line {$err->line}: {$err->message}\");\n    }\n    libxml_clear_errors();\n    exit(1);\n}","typeGuard":"function isWellFormedXml(string $xml): bool {\n    libxml_use_internal_errors(true);\n    $ok = simplexml_load_string($xml) !== false;\n    libxml_clear_errors();\n    return $ok;\n}","tryCatchPattern":"try {\n    $service->import($source, $calendar, $options)->current();\n} catch (\\Exception $e) {\n    // message carries the expat error string and line number; fix that line and retry\n}","preventionTips":["xmllint --noout every generated xCal file in CI","Escape dynamic text with htmlspecialchars(..., ENT_XML1)","Verify file checksums after transfer to catch truncation before parsing"],"tags":["xcal","xml","import","nextcloud","php","expat"],"backgroundTag":"xml-parse-error","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}