{"record":{"id":"e1fdc71ba0e22b42","repo":"jenkinsci/jenkins","slug":"malformed-plugin-attribute-0","errorCode":null,"errorMessage":"Malformed plugin attribute: {0}","messagePattern":"Malformed plugin attribute: (.+?)","errorType":"exception","errorClass":"SAXException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/hudson/PluginManager.java","lineNumber":2311,"sourceCode":"    }\n\n    /**\n     * Parses configuration XML files and picks up references to XML files.\n     */\n    public Map<String, VersionNumber> parseRequestedPlugins(InputStream configXml) throws IOException {\n        final Map<String, VersionNumber> requestedPlugins = new TreeMap<>();\n        try {\n            SAXParserFactory spf = SAXParserFactory.newInstance();\n            spf.setFeature(\"http://apache.org/xml/features/disallow-doctype-decl\", true);\n            spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);\n            spf.newSAXParser().parse(configXml, new DefaultHandler() {\n                @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {\n                    String plugin = attributes.getValue(\"plugin\");\n                    if (plugin == null) {\n                        return;\n                    }\n                    if (!plugin.matches(\"[^@]+@[^@]+\")) {\n                        throw new SAXException(\"Malformed plugin attribute: \" + plugin);\n                    }\n                    int at = plugin.indexOf('@');\n                    String shortName = plugin.substring(0, at);\n                    VersionNumber existing = requestedPlugins.get(shortName);\n                    VersionNumber requested = new VersionNumber(plugin.substring(at + 1));\n                    if (existing == null || existing.compareTo(requested) < 0) {\n                        requestedPlugins.put(shortName, requested);\n                    }\n                }\n\n                @Override public InputSource resolveEntity(String publicId, String systemId) throws IOException,\n                        SAXException {\n                    return RestrictiveEntityResolver.INSTANCE.resolveEntity(publicId, systemId);\n                }\n\n            });\n        } catch (SAXException x) {\n            throw new IOException(\"Failed to parse XML\", x);","sourceCodeStart":2293,"sourceCodeEnd":2329,"githubUrl":"https://github.com/jenkinsci/jenkins/blob/2e228ff40b14dbc8b14ffbc6edf0e4383cf744fc/core/src/main/java/hudson/PluginManager.java#L2293-L2329","documentation":"SAXException thrown when parsing config.xml and encountering a 'plugin' attribute that does not match the pattern [^@]+@[^@]+ (i.e., must contain exactly one '@' separating short name and version, with no additional '@' characters). This validates dependency declarations embedded in XML configuration attributes.","triggerScenarios":"A plugin attribute in config.xml (e.g., plugin='foo' or plugin='foo@1.0@beta') that lacks the required 'shortName@version' format — either no '@', or more than one '@'.","commonSituations":"Manually edited config.xml with malformed plugin attributes, a plugin that writes incorrect dependency specifiers, or migration from an older format that used a different separator.","solutions":["Fix the plugin attribute to use 'shortName@version' format (e.g., 'git@4.0.0').","Remove malformed plugin attributes from config.xml if they are not needed.","Validate config.xml against the expected format before loading."],"exampleFix":"<!-- before -->\n<builder class=\"hudson.tasks.Shell\" plugin=\"git\"/>\n\n<!-- after -->\n<builder class=\"hudson.tasks.Shell\" plugin=\"git@4.0.0\"/>","handlingStrategy":"validation","validationCode":"private static final Pattern PLUGIN_ATTR = Pattern.compile(\"[^@]+@[^@]+\");\npublic static boolean isValidPluginAttribute(String attr) {\n    return attr != null && PLUGIN_ATTR.matcher(attr).matches();\n}","typeGuard":"public static boolean isValidPluginAttribute(String attr) {\n    if (attr == null) return false;\n    int count = 0;\n    for (char c : attr.toCharArray()) if (c == '@') count++;\n    return count == 1;\n}","tryCatchPattern":"try {\n    Map<String, VersionNumber> deps = pluginManager.parseRequestedPlugins(configXml);\n} catch (IOException e) {\n    if (e.getCause() != null && e.getCause().getMessage().contains(\"Malformed plugin attribute\")) {\n        listener.error(\"Config XML has malformed plugin attribute; expected 'name@version'.\");\n    } else {\n        throw e;\n    }\n}","preventionTips":["Use 'shortName@version' format in all plugin attributes.","Validate config.xml plugin attributes before loading or committing.","Avoid manual edits that break the @ separator."],"tags":["plugin-manager","xml","parsing","configuration","validation"],"backgroundTag":null,"analyzedSha":"2e228ff40b14dbc8b14ffbc6edf0e4383cf744fc","analyzedAt":"2026-08-14T07:07:15.274Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}