{"record":{"id":"820bed6fbe038680","repo":"flxzt/rnote","slug":"failed-to-parse-color-attribute-in-xoppstroke-wi","errorCode":null,"errorMessage":"failed to parse `color` attribute in XoppStroke with node id {:?}, could not find attribute","messagePattern":"failed to parse `color` attribute in XoppStroke with node id (.+?), could not find attribute","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rnote-engine/src/fileformats/xoppformat.rs","lineNumber":723,"sourceCode":"                \"failed to parse `tool` attribute in XoppStroke with node id {:?}, could not find attribute\",\n                node.id()\n            )\n        })? {\n            \"pen\" => {\n                self.tool = XoppTool::Pen;\n            }\n            \"highlighter\" => {\n                self.tool = XoppTool::Highlighter;\n            }\n            \"eraser\" => {\n                self.tool = XoppTool::Eraser;\n            }\n            _ => {}\n        }\n\n        self.color =\n            XoppColor::from_strokecolor_attr_value(node.attribute(\"color\").ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"failed to parse `color` attribute in XoppStroke with node id {:?}, could not find attribute\",\n                    node.id()\n                )\n            })?)?;\n\n        self.fill = if let Some(fill) = node.attribute(\"fill\") {\n            Some(fill.parse::<i32>()?)\n        } else {\n            None\n        };\n\n        self.width = node\n            .attribute(\"width\")\n            .ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"failed to parse `width` attribute in XoppStroke with node id {:?}, could not find attribute\",\n                    node.id()\n                )","sourceCodeStart":705,"sourceCodeEnd":741,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-engine/src/fileformats/xoppformat.rs#L705-L741","documentation":"Thrown by XoppStroke::load_from_xml when a `<stroke>` element has no `color` attribute. The parser passes the attribute value to XoppColor::from_strokecolor_attr_value, and it uses ok_or_else to raise this error (with the node id) when the attribute is absent, since a stroke color is mandatory in the XOPP format.","triggerScenarios":"Importing a .xopp file whose `<stroke>` element is missing the `color` attribute — typically hand-edited files, scripts generating xopp XML without color, or files edited by non-Xournal++ tools that dropped the attribute.","commonSituations":"Custom export/generation scripts producing incomplete stroke XML; manual XML edits removing color; corrupted or partially written files.","solutions":["Add a color attribute to the stroke element, e.g. color=\"black\" or color=\"#ff0000ff\".","Regenerate the file ensuring every <stroke> element includes color.","Open and re-save the file in Xournal++ to restore required attributes, then re-import.","Validate the XML against the XOPP schema/known attribute set before importing."],"exampleFix":"// before (file XML)\n<stroke tool=\"pen\" width=\"2.26\">...</stroke>\n// after (file XML)\n<stroke tool=\"pen\" width=\"2.26\" color=\"black\">...</stroke>","handlingStrategy":"validation","validationCode":"fn stroke_elements_have_color(xml: &str) -> Result<(), String> {\n    for (i, chunk) in xml.split(\"<stroke\").skip(1).enumerate() {\n        let head = chunk.split('>').next().unwrap_or(\"\");\n        if !head.contains(\"color=\") {\n            return Err(format!(\"stroke #{i} missing `color` attribute\"));\n        }\n    }\n    Ok(())\n}","typeGuard":"fn stroke_attrs_ok(attrs: &[(&str, &str)]) -> bool {\n    attrs.iter().any(|(k, _)| *k == \"color\")\n}","tryCatchPattern":"match import_xopp(path) {\n    Err(e) if e.to_string().contains(\"`color` attribute in XoppStroke\") => {\n        eprintln!(\"A stroke element is missing its color; fix the XML or re-save in Xournal++: {e:#}\");\n    }\n    Err(e) => eprintln!(\"import failed: {e:#}\"),\n    Ok(doc) => { /* proceed */ }\n}","preventionTips":["When scripting xopp generation, always emit tool, color, and width on every <stroke>","Use a checklist of mandatory XOPP attributes in your generator code","Round-trip generated files through Xournal++ to verify validity"],"tags":["parsing","xml","missing-attribute","xopp"],"backgroundTag":"missing-required-argument","analyzedSha":"bbc5354502ba2fc83eec2670b535348825e679a6","analyzedAt":"2026-09-08T13:20:33.747Z","contentChangedAt":"2026-09-08T13:20:33.747Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}