ANTIGRAVITY LABEN
記事一覧/Tips & 活用術
Tips & 活用術/2026-08-21初級

Antigravity CLI の失敗を握りつぶしていたのは呼び出し側のパイプでした

CLI 1.1.14 で言語サーバーの失敗が非ゼロ終了として表に出るようになりました。ところが自動化のラッパー側が終了コードを受け取れていないと、この改善は届きません。bash 5.1 で16通りの呼び出し方を実際に試し、失敗が消える書き方と直し方をまとめました。

antigravity-cli11automation52bashci-cd16exit-code

CLI 1.1.14 のリリースノートに、目立たない一行があります。言語サーバーが起動時または実行中に失敗したとき、これまで CLI は何も表示せずに終了していました。今後はターミナルに記録され、終了コードも非ゼロになります。

つまり、これまで「成功として扱われていた失敗」が表に出るようになったわけです。

私はこれを読んで、自分の定期実行スクリプトを見に行きました。個人開発でいくつかのサイトの更新を夜間に走らせていて、その中で CLI 系のコマンドを bash から呼んでいます。もし非ゼロ終了が返ってくるようになったのなら、翌朝には赤いログが増えているはずです。

翌朝、ログは前日と同じ緑のままでした。

改善が届いていないのではなく、私の呼び出し方が受け取れていなかったのです。

呼び出し方によって、失敗は消えることがあります

終了コードは、コマンドが返した瞬間に確定します。ただし、そのコマンドをどう書いたかによって、シェルが最終的に報告する値は別のものに置き換わります。

いちばん分かりやすいのはパイプです。

antigravity-cli run ... | head -1

この一行が返す終了コードは、head のものです。左側が何番で落ちていようと、head が正常に1行読めた時点で 0 になります。エラーメッセージは 2>/dev/null で捨てているか、ログの奥に流れていて、誰も見ていません。

これは bash の仕様であって、不具合ではありません。ただ、自動化のラッパーを書くときには、この仕様が「失敗の消しゴム」として働きます。

手元の16通りで、どれが失敗を伝えるのか確かめました

推測で直しても仕方がないので、実際に測りました。終了コード 1 で終わる小さなスクリプトを用意し、それを16通りの書き方で呼んで、シェルが最終的に何を返すかを記録します。

検証用のスクリプトはこれだけです。

#!/bin/bash
echo "Starting language server..."
echo "language server exited unexpectedly (code 1)" >&2
exit 1

環境は GNU bash 5.1.16 です。結果を並べます。

呼び出し方返った終了コード失敗が伝わるか
cmd(そのまま)1伝わる
cmd | head -10消える
set -o pipefailcmd | head -11伝わる
OUT=$(cmd)(トップレベル)1伝わる
RESULT="prefix-$(cmd)"1伝わる
if cmd; then ... fi伝わる(else に入る)
set -e のスクリプト内で cmd | tail -10消える
関数内の local OUT=$(cmd)0消える
関数内の declare OUT=$(cmd)0消える
export OUT=$(cmd)0消える
cmd | tee run.log0消える
cmd | while read -r l; do :; done0消える
cmd && next1伝わる(next は動かない)
バックグラウンド起動+wait1伝わる
timeout 10 cmd1伝わる
cmd | head -1 の後に ${PIPESTATUS[0]}0(配列は 1)読めば伝わる

16通りのうち7つで、失敗が消えました。しかも消えた7つは、どれも「よくやる書き方」です。ログを残したいから tee を挟む、値を関数の中で受けたいから local を付ける、出力を1行ずつ処理したいから while read に流す。悪い書き方をした覚えがないのに、結果だけが静かに 0 になっています。

私のスクリプトで該当していたのは teelocal の2つでした。

消える7つは、3つの原因に集約できます

表を眺めていると、消える条件には共通点があることが分かります。

原因1: パイプの右端が結果を上書きする

| head| tail| tee| while read — いずれもパイプの右側が最後のコマンドになります。シェルが報告するのはその右側の終了コードです。左が落ちたことは、${PIPESTATUS[0]} を明示的に読まないかぎり残りません。

原因2: 変数宣言そのものが「成功したコマンド」になる

localdeclareexport は、それ自体がひとつのコマンドです。local OUT=$(cmd) と書くと、シェルは「local の実行に成功した」と判断します。右辺の $(cmd) が何番で落ちたかは、local の成功で上書きされます。

これはトップレベルの OUT=$(cmd) とは挙動が違います。実測でも、トップレベルは 1 を返し、関数内の local は 0 を返しました。同じ形に見えて結果が逆になるので、私はここでいちばん長く勘違いしていました。

原因3: set -e はパイプの途中を見ていない

set -e を書いてあるから大丈夫だと、私自身がずいぶん長く思い込んでいた箇所がありました。実際に試すと、set -e のスクリプトの中で cmd | tail -1 と書いた行は素通りし、後続の処理まで到達したうえでスクリプト全体が 0 で終わりました。set -e が反応するのは、あくまでシェルが受け取った終了コードです。パイプで 0 に化けた後では、止まる理由がありません。

実際に直した書き方と、その効果

原因ごとに直し方を試して、同じ検証スクリプトで測り直しました。

直す前直した後結果
cmd | head -1先頭に set -o pipefail0 → 1
local OUT=$(cmd)local OUTOUT=$(cmd) の2行に分ける0 → 1
cmd | tee run.logcmd > >(tee run.log)0 → 1
cmd | while read ...set -o pipefail と併用0 → 1
set -e のみset -euo pipefail0 → 1(後続に到達しなくなる)
パイプで整形いったんファイルに落として RC=$?1(出力も1行保持)

local の2行分けは、見た目の変化が小さいわりに効きます。

# 失敗が消える
check_agent() {
  local OUT=$(antigravity-cli run --headless "$1")
  return $?
}
 
# 失敗が伝わる
check_agent() {
  local OUT
  OUT=$(antigravity-cli run --headless "$1")
  return $?
}

宣言と代入を分けると、$? が見るのは代入の結果になります。実測でも 0 から 1 に変わりました。

tee をプロセス置換に変える書き方も、ログを捨てずに済むので気に入っています。

antigravity-cli run --headless "$TASK" > >(tee run.log) 2>&1
echo "exit=$?"   # 1 が返るようになります

ひとつ注意があります。set -o pipefail> >(...) も bash の機能で、sh(dash)では使えません。手元で sh -c 'set -o pipefail' を実行すると Illegal option -o pipefail で拒否されました。CI の実行シェルが /bin/sh になっている場合は、シバンを #!/bin/bash にするか、中間ファイル方式に寄せるほうが確実です。

更新の前に、1分でできる棚卸し

CLI を上げる前に、呼び出し側を先に点検しておくと、更新後のログを素直に信じられるようになります。手元では次の2行で足りました。

grep -rn 'antigravity[^|]*|' ./scripts | grep -v pipefail
grep -rn 'local .*=\$(\|declare .*=\$(\|export .*=\$(' ./scripts

1行目がパイプで受けている箇所、2行目が宣言と代入を同時に書いている箇所です。どちらもヒットがゼロなら、CLI 1.1.14 の非ゼロ終了はそのまま自動化まで届きます。ヒットしたら、上の表の対応を当てるだけです。

私はこの点検を、CLI を上げる作業そのものより先に置くようにしました。監視の側が壊れたまま監視対象を直しても、直ったかどうかを確かめる手段が残らないからです。同じ「無言の失敗」に別の形で出会った経験は、AIエージェントの git push が成功表示なのにリモートへ反映されないときの原因と対処 にも書きました。CLI そのものの検証を先にしたい場合は、MCP サーバーが1本も起動しない原因は、起動前の 40 行で特定できます が近い話です。

次に自動化スクリプトを開くときは、grep の1行目だけでも走らせてみてください。ヒットが1件もなければ、それはそれで安心材料になります。

最後までお読みいただき、ありがとうございました。

シェア

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

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

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

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

関連記事

Tips & 活用術2026-06-14
Antigravity CLI に乗り換えて「速くなった気がする」を、hyperfine で数値にする
Go 製になった Antigravity CLI の起動が速い、という体感を hyperfine で再現可能な数値に落とし込みます。ウォーム/コールドの測り分け、スケジュール実行での積み上がり、回帰を防ぐ CI ゲートまで。
連携・プラグイン2026-06-13
Antigravity CLI(agy)を CI で非対話実行する — stdout が消える非TTY問題への対処
agy -p を GitHub Actions や cron で動かすと、ローカルでは見えていた出力が空になり、終了コードだけ 0 が返ることがあります。非TTY検出のしくみと、擬似TTY・テキスト出力の堅牢なパース・APIキー認証で確実に結果を受け取る設計をまとめます。
Tips & 活用術2026-07-10
エージェントへの依頼は、送信ボタンを押す前に大きさを測れる — 差し戻し率から逆算するタスク分割
Antigravity のエージェントが的外れなコードを返すとき、原因はプロンプトの文言ではなくタスクの大きさにあります。依頼文を送信前に採点する Python スコアラーを実装し、80件の依頼ログを後ろ向きに採点して差し戻し率との相関を確かめた記録です。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →