ANTIGRAVITY LABEN
記事一覧/Editor View
Editor View/2026-08-23中級

WebP と Opus の添付が通らないときの切り分け順序

8月20日のハブ 2.9.1 で WebP の添付に対応し、同じ日の CLI 1.1.17 では Ogg 系の音声がモデルに拒否される問題が直りました。拒否がファイル側ではなく送信側の MIME 判定で起きる仕組みを、手元で実際に測った判定結果とともに切り分けます。

Antigravity354MIME添付ファイルトラブルシューティング29

壁紙アプリの新しい書き出しを WebP でまとめ、命名規則の揺れを拾ってもらうつもりでエージェントに添付しました。返ってきたのは沈黙です。エラーも出ず、添付そのものが無かったことになっていました。

同じ週に、癒し系アプリの効果音を .opus で渡したときは、扱えない形式だという返答が来ました。ファイルはどちらも手元のプレイヤーで問題なく開けます。

症状は違いますが、原因の層は同じでした。ファイルの中身ではなく、送り出す側がそのファイルに何の MIME を貼ったか、です。

8月20日のハブ 2.9.1 で WebP の添付が加わり、同じ日の CLI 1.1.17 では .ogg / .opus / .ogv が汎用の Ogg として送られてモデルに拒否される問題が修正されました。直ったこと自体より、なぜそうなっていたのかを掴んでおくほうが、次に別の拡張子で詰まったときに効きます。

拒否は3つの層のどこかで起きています

添付が届かないと分かった時点で、いきなりファイルを変換し直すのは遠回りです。まず、どの層で落ちているかを決めます。

表に出る症状最初に見るもの
①クライアントと版添付しても無反応。会話に何も残らないハブ / IDE / CLI の版と、その版で対応した形式
②MIME の決定添付は通るが、モデルの側が扱えないと返す手元での判定結果(下記の2通り)
③受け渡しの経路会話が重くなる、参照だけが残る直接添付か、MCP サーバーの戻り値か

私がつまずいた2件は、それぞれ①と②でした。WebP は 2.9.1 より前のハブでは受け口そのものが無く、Opus は受け口はあるのに貼られた MIME が粗すぎたわけです。Windows で画像やメディアの添付が失敗していた問題も①の側で、IDE 2.5.5(8月13日)で直っています。

①は版を上げれば終わります。厄介なのは②で、こちらは版を上げても、環境によっては再発します。理由は次の節で測ります。

手元の判定を2通り並べると、食い違いが見えます

MIME の決め方には、中身の先頭バイトを見る方法と、拡張子の対応表を引く方法の2通りがあります。同じファイルに両方を当てて、結果を並べてみました。

# 中身から判定する(マジックバイト)
for f in sample.png sample.webp sample.ogg sample.opus sample.ogv; do
  printf '%-13s %s\n' "$f" "$(file -b --mime-type "$f")"
done

Ubuntu 22.04・file 5.41 での出力です。

sample.png    image/png
sample.webp   image/webp
sample.ogg    audio/ogg
sample.opus   audio/ogg
sample.ogv    audio/ogg

次に、拡張子の表を引く側です。

import mimetypes
 
for name in ["sample.png", "sample.webp", "sample.ogg", "sample.opus", "sample.ogv"]:
    print(f"{name:<13} {mimetypes.guess_type(name)[0]}")

Python 3.10.12 での出力です。

sample.png    image/png
sample.webp   None
sample.ogg    audio/ogg
sample.opus   audio/ogg
sample.ogv    video/ogg

並べると、二重の穴が見えてきます。

ファイル中身から拡張子から起きること
sample.webpimage/webp(決まらない)表を引く実装では型が付かず、汎用のバイト列として送られる
sample.opusaudio/oggaudio/ogg両方一致するが、Opus であることが落ちている
sample.ogvaudio/oggvideo/ogg音声と映像で判定が割れる

.ogg / .opus / .ogv の3つは、先頭4バイトがいずれも 4f676753OggS)でした。同じコンテナに包まれている以上、中身を覗く方法では区別のしようがありません。中身から判定する実装が Ogg 系をまとめて粗い型で送っていた、という挙動には、こういう素直な理由があります。

WebP のほうは逆で、拡張子の表に行が無いために型が決まりません。決まらないものを送る側は、たいてい汎用のバイト列として扱います。受け取ったモデルからすれば、画像として渡されていないわけですから、見ないという判断は筋が通っています。

Opus が汎用の Ogg になるのは、機械側の表が勝つからです

ここがいちばん引っかかった点でした。Python の内蔵テーブルは、.opus に対して audio/opus を持っています。ところが初期化を通すと、値が入れ替わります。

import mimetypes
 
print(mimetypes.types_map.get(".opus"))   # 初期化前: audio/opus
mimetypes.init()
print(mimetypes.types_map.get(".opus"))   # 初期化後: audio/ogg

上書きしているのは、その機械に置かれている対応表です。手元の /etc/mime.types を見ると、こう書かれていました。

audio/ogg					oga ogg opus spx

opusaudio/ogg の行に同居しています。内蔵の値より、機械に置かれた表のほうが後から読まれる分だけ強い、という順序です。

この構造が意味するのは、同じコードでも機械が変われば結果が変わる、ということです。手元では通っていた添付が、対応表を持たない小さなコンテナや CI の中では別の型になる。個人開発で作業機とビルド環境を行き来していると、この差はまとまった時間を溶かします。私自身、最初は書き出し設定のほうを何度も疑いました。

MCP 経由で来るファイルは、判定する主体が違います

三つ目の層です。エージェントにファイルが渡る経路は、人が添付するものだけではありません。MCP サーバーが画像や PDF を返してくることもあります。

この経路では、型を決めるのは手元のクライアントではなく、サーバー側の宣言です。手元でいくら判定を揃えても届きません。切り分けの順序としては、まず「そのファイルは自分が添付したのか、ツールが返したのか」を確かめる必要があります。

ハブ 2.9.1 では、接続済みの MCP ツールが返す大きなバイナリは会話へ展開されず、ファイルへ保存されて参照される形になりました。以前は画像や PDF が会話の中で展開され、そのターンごと壊れることがあった部分です。会話に本文が見えず参照だけが残っている場合、それは不具合ではなく、この保存方式が働いた結果です。

MCP サーバーの起動段階で落ちている場合は、型の話に入る前の問題です。そちらはMCP サーバーが1本も起動しない原因は、起動前の 40 行で特定できますでまとめています。

添付する前に通しておく短い確認

②の層は、渡す前に手元で潰せます。2通りの判定を突き合わせ、食い違いと欠落を先に出すだけの短いスクリプトです。

#!/usr/bin/env python3
"""添付予定のファイルについて、中身と拡張子それぞれの MIME 判定を突き合わせる。"""
import mimetypes
import subprocess
import sys
 
# 中身からは区別できず、判定が揃っていても粗いままになる拡張子
COARSE = {".opus": "audio/opus", ".ogv": "video/ogg", ".oga": "audio/ogg"}
 
 
def by_content(path):
    try:
        r = subprocess.run(["file", "-b", "--mime-type", path],
                           capture_output=True, text=True, check=True)
        return r.stdout.strip()
    except (subprocess.CalledProcessError, FileNotFoundError):
        return None
 
 
def main(paths):
    flagged = 0
    for p in paths:
        ext = "." + p.rsplit(".", 1)[-1].lower() if "." in p else ""
        by_ext = mimetypes.guess_type(p)[0]
        by_con = by_content(p)
        if by_ext is None:
            print(f"[表に無い] {p}: 拡張子から決まりません(中身は {by_con})")
            flagged += 1
        elif by_con and by_ext != by_con:
            print(f"[食い違い] {p}: 拡張子 {by_ext} / 中身 {by_con}")
            flagged += 1
        elif ext in COARSE and by_ext != COARSE[ext]:
            print(f"[粗い]     {p}: {by_ext} と出ますが、本来は {COARSE[ext]} です")
            flagged += 1
        else:
            print(f"[一致]     {p}: {by_ext}")
    print(f"--- 要確認 {flagged} 件 / 全 {len(paths)} 件 ---")
    return 1 if flagged else 0
 
 
if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))

先ほどの5ファイルに通した結果です。

[一致]     sample.png: image/png
[表に無い] sample.webp: 拡張子から決まりません(中身は image/webp)
[一致]     sample.ogg: audio/ogg
[粗い]     sample.opus: audio/ogg と出ますが、本来は audio/opus です
[食い違い] sample.ogv: 拡張子 video/ogg / 中身 audio/ogg
--- 要確認 3 件 / 全 5 件 ---

COARSE の3行を足したのは、判定が一致していることと、その値が正しいことが別だと分かったからです。.opus は両方が audio/ogg で揃います。揃っているのに、Opus であるという情報は落ちています。一致だけを合格条件にすると、この一件を素通りさせてしまいます。

終了コードを非ゼロで返しているのは、添付を伴う自動処理の手前に置けるようにするためです。App Store 提出前のアセット確認のように、まとめて機械へ渡す工程では、通す前に止まってくれるほうが助かります。

次に確かめる一手

手元で添付する予定のファイルを、まずこのスクリプトに通してみてください。[表に無い] が出た拡張子だけが、対応表を持たない環境で型を失う候補です。そこだけを、クライアント側で明示的に型を指定するか、対応の確かな形式で書き出し直すかに振り分ければ済みます。

版を上げること自体は、いつでもできます。ただ新しい版は順に配られるため、手元へ届くまで数日かかることもあります。届くまでの間、どの層で落ちているかを判別できる状態にしておくと、待っている時間が観察の時間に変わります。

複数の機械を行き来する構成そのものをどう決めるかは、Remote Control をホスト機に入れる判断は、daemon の生存条件と遮断経路で決まりますで、資格情報の置き場所と併せて掘り下げました。今回の話は、その構成の上を実際に流れるファイルの側です。

小さな引っかかりでしたが、掴んでみると同じ形の問題を先回りできるようになりました。お読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Editor View2026-08-13
フックの待ち方が 2.6.0 で変わりました。終わらないターンを切り分ける手順
エージェントのターンが終わらないとき、原因がモデルではなく自分の書いたフックにあることがあります。IDE 2.6.0 で変わった打ち切りの扱いを確認し、止まり得るフックを洗い出す監査スクリプトと、フック側に打ち切りを持たせる書き方をまとめました。
Editor View2026-04-08
Antigravity エディタでよくある問題と解決法:補完停止・接続エラー・プロジェクト読み込み失敗
AI 補完の停止、プロジェクトが開けない、ターミナルの無応答、Git 統合の不調、拡張機能のトラブル——Antigravity エディタで頻発する5つの問題の解決法をまとめました。設定リセットや接続確認など、順に試せる手順の形で説明しています。
Editor View2026-08-01
売上CSVを添付したら、いちばん高くついていたのは日付の列でした — 添付形式のトークン単価を測る
2.4.3 で .json・.md・.csv を入力欄へ添付できるようになりました。同じ表を8つの形で書き出してトークンで並べ、列ごとに値段をつけ、要約へ落とすまでを実測しています。数字はすべて手元で走らせて取得したものです。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →