draw.io が「d.setId is not a function」で開けない — AI の誤診を抜けて真因(id と Array.prototype の衝突)に辿り着くまで

著者: なぎゆー公開日: 2026-06-13最終更新: 2026-07-24読了目安: 約 6 分
カテゴリ:開発スタック
JavaScriptデバッグAIdraw.io

AI に作らせた図を手元で開こうとしたら d.setId is not a function で開けない。Web 版でも VSCode 拡張でも壊れる。AI は「ブラウザなら」「拡張がおかしい」と環境を疑わせたが全部ハズレで、履歴を捨てて白紙から調べ直させてやっと真因に辿り着いた。犯人はセルの id が push だったこと——Array.prototype とのプロトタイプ衝突だった。

はじめに

これは draw.io のバグ解説というより、AI に何度も見当違いの原因を掴まされて、最後に「白紙から調べ直させる」ことで抜けたという、デバッグの回り道の話だ。オチの技術的な真因(セルの id が push だったせいで壊れる)は最後に書くが、そこに至るまでが長かった。

前提:図はこう作っている

うちのリポジトリでは、説明図を全部手で描いているわけではない。AI に .drawio の素体を作らせ、それを手元で開いて .drawio.svg に書き出し、記事の Markdown に埋め込む——という運用にしている。図の「元データ」は AI 産、仕上げと変換は手元、という分担だ。

だから「手元で .drawio が開けない」は、単なる不便では済まない。変換ステップが止まって、記事に図を載せられなくなる。今回まさにそこで詰まった。

詰まり①:どのクライアントで開いても壊れる

問題の図は、GitHub Actions のモノレポデプロイフローを描いたものだった。クローンは手元にあるので、まず素直に開こうとした。

  • Web 版(diagrams.net)で開いた → d.setId is not a function が出て、図が一切表示されない。
  • VSCode の draw.io 拡張で開いた(いつも使っている拡張機能をそのまま) → こちらも同じく壊れて表示できない。

この時点で AI に相談すると、返ってくるのは環境を疑う筋だった。「ブラウザ版なら開けるはず」「その VSCode 拡張の調子がおかしいのでは」。もっともらしいので順に潰したが、どちらも実際に試して駄目だった。Web 版でも拡張でも同じエラーが同じように出る。ここで少なくとも「自分の環境やクライアント固有の問題」ではなく、ファイルの中身に原因があることは確定した。にもかかわらず、会話は環境の周りをぐるぐる回り続けた。

詰まり②:同じ文脈で聞き続けても堂々巡り

環境要因を全部潰しても直らないと分かった頃、AI の側にも手詰まり感が出てきた。ここで気づいたのは、同じ会話の文脈で問い続ける限り、最初に立てた「環境が怪しい」という筋を引きずってしまうということだ。人間なら「一晩寝て仕切り直す」ところを、AI なら履歴ごと捨ててやり直せる。

そこで、それまでのやり取りを一切引き継がない、まっさらなコンテキストで一から調べ直させることにした。先入観のない目で、エラーの発生源そのものを追わせる。これが効いた。

真因:id が push だったこと

白紙で追い直させて出てきた答えはこうだった。

draw.io は内部で mxGraph を使い、XML のデコードに mxCodec を通す。このデコーダは「id → デコード済みオブジェクト」のキャッシュを持つが、それがプレーンな配列で初期化されている(this.objects = [] 相当)。デコード中に既存オブジェクトを引くとき、dec.objects[id] という形でキャッシュを参照する。

問題の図には、id="push" のセルがあった。CI フローの図なので、ごく自然に「push」というステップをそのままセルの id にしていた。ところが dec.objects["push"] を評価すると、配列にそのキーは無いのに、プロトタイプチェーンを辿って Array.prototype.push という関数が返ってしまう。

その結果、本来 mxCell であるべきオブジェクトが「push 関数」に化ける。続く処理で obj.setId(...) が呼ばれても、関数に setId は無いので d.setId is not a function(d は push 関数)で落ちる——というのが全体像だった。他の図が普通に開けていたのは、draw.io が保存時に xxxx-1 のような衝突しない id を自動採番していたからだ。手で「push」と付けた図だけが地雷を踏んでいた。

最小再現はこれだけで済む。

<?xml version="1.0" encoding="UTF-8"?>
<mxGraphModel>
  <root>
    <mxCell id="0" />
    <mxCell id="1" parent="0" />
    <mxCell id="push" value="問題のセル" vertex="1" parent="1">
      <mxGeometry x="100" y="100" width="120" height="60" as="geometry" />
    </mxCell>
  </root>
</mxGraphModel>

id="push" を push-node のような名前に変えるだけで、あっさり開けるようになる。実際の修正も、図の中の push という id を改名しただけだった。そのセルを source / target で参照している edge があれば、そちらも一緒に直す。

持ち帰り

技術的な教訓は一行だ。任意のキーを受け取るハッシュマップに、プレーンなオブジェクトや配列を使うとプロトタイプのメンバ名と衝突する。push・constructor・hasOwnProperty などがキーに来ると、意図せず関数が返る。用途がハッシュマップなら最初から Map を使えばいい(Map はプロトタイプを一切参照しないので、キーが何でも衝突しない)。TypeScript で Record<string, T> と型を付けても、実行時のプロトタイプ解決は止まらない——型が通ることと壊れないことは別だ。今回の真因は mxGraph 側の実装なので自分では直せなかったが、自分のコードで同じ轍を踏まないための教訓としては十分効く。

ただ、今回いちばん身に沁みたのはそっちではない。AI に詰まらされたとき、同じ文脈で粘るより、履歴を捨てて白紙から調べ直させる方が早いことだ。最初に立った仮説(ここでは「環境が怪しい」)は、会話を続けるほど強化されて抜けにくくなる。しかも「ブラウザなら」「拡張がおかしい」のように、原因を自分の外(環境)に押しつける誤診はとても出やすい。環境要因を一通り潰しても直らないなら、そこが仕切り直しの合図だ。人間相手なら難しい「先入観のリセット」を、AI なら文脈ごと捨てて安く実行できる——これは AI と組んでデバッグするときの、地味だが効く一手だと思う。


関連記事