DayuanJiang/next-ai-draw-io · error · Error

addPageToDoc: opts.xml must be a bare <mxGraphModel>.

Error message

addPageToDoc: opts.xml must be a bare <mxGraphModel>.

What it means

After addPageToDoc builds a wrapper snippet ('<wrapper><diagram id=... name=...>MODEL</diagram></wrapper>') from user-supplied opts.xml and parses it, the parser reported an error node. This means the assembled XML is malformed — almost always because opts.xml (the mxGraphModel) contains invalid XML such as unescaped entities, mismatched tags, or stray characters.

Source

Thrown at packages/mcp-server/src/pages.ts:263

        if (isMxFile(trimmed)) {
            throw new Error(
                "addPageToDoc: opts.xml must be a bare <mxGraphModel>; received a full <mxfile>. Extract the target diagram's <mxGraphModel> first.",
            )
        }
        if (!isMxGraphModel(trimmed)) {
            throw new Error(
                "addPageToDoc: opts.xml must be a bare <mxGraphModel>.",
            )
        }
        inner = trimmed
    } else {
        inner = `<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/></root></mxGraphModel>`
    }

    const snippet = `<wrapper><diagram id="${escapeAttr(id)}" name="${escapeAttr(name)}">${inner}</diagram></wrapper>`
    const tempDoc = new DOMParser().parseFromString(snippet, "text/xml")
    if (tempDoc.querySelector("parsererror")) {
        throw new Error(
            "Failed to parse new page xml — make sure it is a valid <mxGraphModel>",
        )
    }
    const newDiagram = tempDoc.querySelector("diagram")
    if (!newDiagram) {
        throw new Error("Failed to construct <diagram> element for new page")
    }

    const imported = doc.importNode(newDiagram, true) as Element
    doc.documentElement.appendChild(imported)

    return {
        id,
        name,
        index: existing.length,
        cellCount: imported.querySelectorAll("mxCell").length,
    }
}

View on GitHub (pinned to 155ef4f7ac)

Solutions

  1. Escape dynamic text with escapeAttr/escapeXml-equivalents before embedding in XML
  2. Validate opts.xml parses standalone before calling addPageToDoc
  3. Replace HTML-only entities (&nbsp;) with numeric XML equivalents (&#160;) or wrap labels as ...escaped HTML per mxGraph conventions
  4. If concatenating, use DOM APIs (createElement/setTextContent) instead of strings

Example fix

// before
addPageToDoc(doc, { xml: `<mxGraphModel><root><mxCell value="A & B" .../></root></mxGraphModel>` })

// after
const value = 'A &amp; B'
addPageToDoc(doc, { xml: `<mxGraphModel><root><mxCell value="${value}" .../></root></mxGraphModel>` })
Defensive patterns

Strategy: validation

Validate before calling

const probe = new DOMParser().parseFromString(opts.xml, 'text/xml')
if (probe.querySelector('parsererror')) {
  throw new Error('opts.xml is not well-formed XML; escape entities/quotes first')
}

Type guard

function isWellFormedXml(s: string): boolean {
  return !new DOMParser().parseFromString(s, 'text/xml').querySelector('parsererror')
}

Try / catch

try { addPageToDoc(doc, { xml }) } catch (e) { if ((e as Error).message.includes('Failed to parse')) throw new Error(`page xml invalid near: ${xml.slice(0, 120)}`) else throw e }

Prevention

When it happens

Trigger: Passing opts.xml containing raw '&' or '<' in attributes/values (e.g. a label 'A & B'), truncated markup, or HTML-style entities like &nbsp; that strict XML parsing rejects.

Common situations: Building mxGraphModel strings by concatenation without escaping user text; embedding HTML in cell labels unescaped; copy-pasting from rendered HTML where entities were already decoded.

Related errors


AI-assisted analysis of DayuanJiang/next-ai-draw-io@155ef4f7ac (2026-08-27). Data as JSON: /api/errors/dfec7cca748dc43f. Report an issue: GitHub.