{"record":{"id":"4476ae7ed2ad20a5","repo":"a-b-street/abstreet","slug":"curvey","errorCode":null,"errorMessage":"curvey({}): {}","messagePattern":"curvey\\((.+?)\\): (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"widgetry/src/text.rs","lineNumber":619,"sourceCode":"                _ => \"\",\n            },\n            fg_color.as_hex(),\n            fg_color.a,\n            start_offset,\n            stroke_parameters,\n        )\n            .unwrap();\n\n        write!(\n            &mut svg,\n            r##\"<textPath href=\"#txtpath\">{}</textPath></text></svg>\"##,\n            htmlescape::encode_minimal(&self.text)\n        )\n        .unwrap();\n\n        let mut svg_tree = match usvg::Tree::from_str(&svg, &usvg::Options::default()) {\n            Ok(t) => t,\n            Err(err) => panic!(\"curvey({}): {}\", self.text, err),\n        };\n        svg_tree.convert_text(&assets.fontdb.borrow());\n        let mut batch = GeomBatch::new();\n        match crate::svg::add_svg_inner(&mut batch, svg_tree, tolerance) {\n            Ok(_) => batch,\n            Err(err) => {\n                error!(\"render_curvey({}): {}\", self.text, err);\n                batch\n            }\n        }\n    }\n}\n","sourceCodeStart":601,"sourceCodeEnd":632,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/widgetry/src/text.rs#L601-L632","documentation":"render_curvey generates an SVG with text placed along a curve and parses it with usvg; Tree::from_str failure panics with the text and parser error. As with render_line, the generated markup or its content failed XML/SVG parsing.","triggerScenarios":"Calling render_curvey with self.text that produces invalid SVG after htmlescape encoding — e.g. strings with invalid XML control characters, or an upstream regression in the generated <path>/<text> markup.","commonSituations":"Curved labels built from user input or map data containing odd bytes; text containing characters that survive htmlescape but are still invalid XML (e.g. most control chars, lone surrogates).","solutions":["Filter out non-XML control characters from the text before calling render_curvey.","Reproduce with the exact text from the panic message and minimize it.","Fall back to render_line for texts that fail curvey parsing."],"exampleFix":"// before\nText::curvey(text, curve);\n// after\nlet safe: String = text.chars().filter(|c| !c.is_control()).collect();\nText::curvey(safe, curve);","handlingStrategy":"validation","validationCode":"let safe: String = text.chars().filter(|c| !c.is_control()).collect();\nassert!(htmlescape::encode_minimal(&safe).chars().all(|c| c as u32 >= 0x20 || c == '\\n'));","typeGuard":null,"tryCatchPattern":"// Fall back to straight-line text if curvey parsing fails:\nmatch renderable_curvey(&safe, curve) { Some(t) => t, None => Text::from(safe) }","preventionTips":["Sanitize curved-label text (control chars, invalid XML chars)","Test curvey labels with real map/user data strings","Keep a fallback plain-text renderer for problematic labels"],"tags":["rust","svg","text-rendering","parsing"],"backgroundTag":"xml-parse-error","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}