{"record":{"id":"2b1b26cce2687721","repo":"dianping/cat","slug":"attribute-equal-must-after-attrname","errorCode":null,"errorMessage":"attribute equal must after attrName","messagePattern":"attribute equal must after attrName","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cat-home/src/main/webapp/assets/js/editor/worker-xml.js","lineNumber":1517,"sourceCode":"\treturn t;\n\t\n}\nfunction parseElementStartPart(source,start,el,entityReplacer,errorHandler){\n\tvar attrName;\n\tvar value;\n\tvar p = ++start;\n\tvar s = S_TAG;//status\n\twhile(true){\n\t\tvar c = source.charAt(p);\n\t\tswitch(c){\n\t\tcase '=':\n\t\t\tif(s === S_ATTR){//attrName\n\t\t\t\tattrName = source.slice(start,p);\n\t\t\t\ts = S_EQ;\n\t\t\t}else if(s === S_ATTR_S){\n\t\t\t\ts = S_EQ;\n\t\t\t}else{\n\t\t\t\tthrow new Error('attribute equal must after attrName');\n\t\t\t}\n\t\t\tbreak;\n\t\tcase '\\'':\n\t\tcase '\"':\n\t\t\tif(s === S_EQ){//equal\n\t\t\t\tstart = p+1;\n\t\t\t\tp = source.indexOf(c,start)\n\t\t\t\tif(p>0){\n\t\t\t\t\tvalue = source.slice(start,p).replace(/&#?\\w+;/g,entityReplacer);\n\t\t\t\t\tel.add(attrName,value,start-1);\n\t\t\t\t\ts = S_E;\n\t\t\t\t}else{\n\t\t\t\t\tthrow new Error('attribute value no end \\''+c+'\\' match');\n\t\t\t\t}\n\t\t\t}else if(s == S_V){\n\t\t\t\tvalue = source.slice(start,p).replace(/&#?\\w+;/g,entityReplacer);\n\t\t\t\tel.add(attrName,value,start);\n\t\t\t\terrorHandler.warning('attribute \"'+attrName+'\" missed start quot('+c+')!!');","sourceCodeStart":1499,"sourceCodeEnd":1535,"githubUrl":"https://github.com/dianping/cat/blob/e815e74d4c2dd74edac831241f1253fcc7d25381/cat-home/src/main/webapp/assets/js/editor/worker-xml.js#L1499-L1535","documentation":"Error from the hand-rolled SAX attribute state machine in the ACE XML worker (worker-xml.js:~1517). The tokenizer walks a start tag character by character with states S_TAG/S_ATTR/S_EQ/S_V/S_E/S_C; an '=' is legal only in S_ATTR (right after an attribute name) or S_ATTR_S. In any other state the '=' is meaningless, so the parser throws to abort the tag scan.","triggerScenarios":"Malformed attribute syntax such as '<tag =foo>' (equals before any name), '<tag a==\"1\">' (double equals), or '<tag a b=\"x\">=' sequences where the scanner meets '=' after a value or quote state. The switch default at line 1513-1515 throws.","commonSituations":"Hand-edited or templated XML/JSP/HTML where an attribute placeholder got mangled, templating engines emitting '=expr' bindings into an XML worker, or partial typing in the editor while the worker re-parses on each keystroke.","solutions":["Fix the tag so every '=' directly follows an attribute name: <tag attr=\"value\">.","Remove stray '=' characters or duplicated equals inside the flagged start tag.","If template syntax (e.g. {{...}} or =expr) is intentional, route the buffer through a worker/mode that understands that template language instead of the XML worker."],"exampleFix":"<!-- before -->\n<div =class=\"box\">...</div>\n\n<!-- after -->\n<div class=\"box\">...</div>","handlingStrategy":"validation","validationCode":"// Before parsing user XML, reject tags with '=' not preceded by a name:\nfunction attributesWellFormed(tagText) {\n  return /^<\\s*[\\w:.-]+(\\s+[\\w:.-]+\\s*=\\s*(\"[^\"]*\"|'[^']*'|\\S+))?\\s*\\/?>$/.test(tagText.trim());\n}","typeGuard":null,"tryCatchPattern":"try {\n  parseStartTag(source, start, tagNameReplacer);\n} catch (ex) {\n  if (/attribute/.test(ex.message)) reportXmlError(ex); else throw ex;\n}","preventionTips":["Always write name=value pairs; '=' never stands alone.","Run xmllint on generated XML in CI.","Remember the worker re-parses per keystroke — transient tag errors are normal while typing."],"tags":["xml","sax","ace-editor","attributes","parser"],"backgroundTag":null,"analyzedSha":"e815e74d4c2dd74edac831241f1253fcc7d25381","analyzedAt":"2026-08-14T14:22:34.512Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}