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

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

Error message

addPageToDoc: opts.xml must be a bare <mxGraphModel>; received a full <mxfile>. Extract the target diagram's <mxGraphModel> first.

What it means

addPageToDoc requires opts.xml to be a single bare <mxGraphModel> element — the body of one page — because it wraps that markup inside a new <diagram> element. Passing a full multi-page <mxfile> would nest files illegally. This guard (isMxFile) fires before the generic shape check at line 251, which throws the same message for the same condition.

Source

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

 * Returns the new PageInfo. Throws if the requested id collides or the xml
 * shape is wrong.
 */
export function addPageToDoc(
    doc: Document,
    opts: { id?: string; name?: string; xml?: string } = {},
): PageInfo {
    const existing = listPagesFromDoc(doc)
    const id = opts.id || generatePageId()
    if (existing.some((p) => p.id === id)) {
        throw new Error(`Page id "${id}" already exists`)
    }
    const name = opts.name || `Page-${existing.length + 1}`

    let inner: string
    if (opts.xml?.trim()) {
        const trimmed = stripXmlDeclaration(opts.xml.trim())
        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>",

View on GitHub (pinned to 155ef4f7ac)

Solutions

  1. Extract the target page's <mxGraphModel> first (see fix below) and pass that as opts.xml
  2. Or use an API designed for whole-file imports if one exists instead of addPageToDoc
  3. If you only need an empty page, omit opts.xml entirely

Example fix

// before
addPageToDoc(doc, { xml: fs.readFileSync('other.drawio', 'utf8') })

// after
const src = new DOMParser().parseFromString(fs.readFileSync('other.drawio', 'utf8'), 'text/xml')
const model = src.querySelector('mxfile > diagram > mxGraphModel')
addPageToDoc(doc, { xml: new XMLSerializer().serializeToString(model) })
Defensive patterns

Strategy: type-guard

Validate before calling

function extractFirstMxGraphModel(mxFileXml: string): string | null {
  const doc = new DOMParser().parseFromString(mxFileXml, 'text/xml')
  const m = doc.querySelector('mxfile > diagram > mxGraphModel') ?? doc.querySelector('mxGraphModel')
  return m ? new XMLSerializer().serializeToString(m) : null
}

Type guard

function isBareMxGraphModel(s: string): boolean {
  return /^\s*(<\?xml[^>]*\?>\s*)?<mxGraphModel[\s>]/i.test(s)
}

Try / catch

try { addPageToDoc(doc, { xml }) } catch (e) { if ((e as Error).message.includes('bare <mxGraphModel>')) { const m = extractFirstMxGraphModel(xml); if (m) addPageToDoc(doc, { xml: m }) else throw e } else throw e }

Prevention

When it happens

Trigger: Calling addPageToDoc(doc, { xml: fullDrawioFileString }) where the string's root element is <mxfile>, e.g. reusing the output of another file or of serializeToString on the whole document.

Common situations: Copy-pasting a .drawio file's contents as the xml option; piping output of one export directly into addPageToDoc; assuming the API accepts whole files rather than page fragments.

Related errors


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