候補は見つかっていたのに、答えを読む所で毎回落ちていた:200 トークンで切れた URL と、ハイフンで 400 になる検索
トレカダッシュ 開発の記録 / 2026年8月
iOS アプリ「トレカダッシュ」の、まとめサイト由来の記事は、お店の公式の告知のページへ、リンクを張り替えます。ところが、開発者モードの報告が、続けて 7 件、来ました。「正しい告知の URL は、これ。ちゃんとやって」。
調べると、候補は見つかっていたのに、答えを読む所で、毎回落ちていました。この記事は、その真因と、同じ時期に見つけた、よく似た 1 件の記録です。
数字は、2026 年 8 月 23 日と 24 日の作業の記録から書いています。
真因:AI の返事が、200 トークンで途中で切れていた
公式の告知ページを選ぶ処理は、最後に AI に、「候補のどれが、そのお店の告知か」を答えさせます。その返事の長さの上限(max_output_tokens)を、200 にしていました。
ところが、返事には、日本語をパーセント符号化した、長い URLが入ります。1 つで 200 文字を超えます。返事は途中で切れ、JSON として読めなくなり、Unterminated string in JSON という例外になっていました。
WARN [応募リンク] 公式ページの判定に失敗 [お宝創庫・プレイズ / …]: Unterminated st…
→ 対象 10 件、すべて「見つからず」
候補は、見つかっていたのです。答えを読むところで、毎回、落ちていました。上限を 800 に上げました。さらに、それでも切れたときに、全部を捨てず、"url": "… の部分だけを拾い直す救済も入れました。
直しても、採用が 0 件だった:AI が指示に忠実だった
JSON を直しても、採用は 0 件でした。ログを見ると、候補はあるのに、「採用なし」です。原因は、AI が、指示に忠実だったことでした。お店の告知は、こういう書き方をします。
「【アプリ会員様限定】再版/ポケモンカードゲーム 4 種 抽選販売に関して」
個別の商品名を書いていません。指示には、「商品が違う場合は、選ぶな」とありました。AI は、それに従って、拒否していたのです。
2 つ直しました。
- 商品名で絞った検索に加えて、いつも、
site:店のドメイン 抽選も検索して、候補にまとめる。(「空振りしたときだけ」では、無関係なページが 1 件返るだけで、発動しませんでした。) - 指示に、「その店自身の告知のページが、複数の商品をまとめて案内していて、個別の名前を書いていないときは、選んでよい」を足す。ただし、ほかの店やまとめサイトは、まとめて案内していても、絶対に選ばない。
結果は、10 件のうち、3 件が、お店の公式の告知のページへ張り替わりました。
もう 1 つ、アプリ限定の店(アプリでしか、抽選の案内を出さない店)のために、「アプリでご確認ください」と出せる一覧を作りました。アプリが実在すると分かっている店だけです。推測で足しません。
よく似た 1 件:楽天のキーワード検索が、ハイフンで丸ごと失敗していた(8 月 24 日)
ログに、「楽天市場の個別弾の検索に失敗しました」が、遊戯王だけで 8 種以上、繰り返し出ていました。調べると、楽天の API は、検索のキーワードのハイフンの使い方によっては、リクエスト自体が 400 wrong_parameter で失敗しました。
| キーワードの形 | 結果 |
|---|---|
"A - B"(ハイフンの前後に空白) | 400 |
"A B "(末尾に空白だけ残る) | 400 |
"A B-"(末尾がハイフンで終わる) | 400 |
"AB-"(空白を挟まない単語 + 末尾ハイフン) | 200(正常) |
遊戯王の商品名は、「LIMIT OVER COLLECTION - THE RIVALS -」のように、副題をハイフンで挟む書き方が多く、この形の弾は、個別弾の検索から、一度も拾えていませんでした。
直し方は、2 段階です。(1) ハイフンの直後の空白を詰める。(2) 末尾に残るハイフンと空白を落とす。「LIMIT OVER COLLECTION - THE RIVALS -」は、「LIMIT OVER COLLECTION -THE RIVALS」になり、200 を確かめました。同じ問題は、楽天ブックスの API でも、起きることを確かめたので、同じ整形を、キーワードを渡す 4 つの関数に当てました。
分かったこと
候補が見つかっているのに、結果が 0 件のときは、答えを読む所を疑う。
- 返事の長さの上限は、返す中身から決める。パーセント符号化した URL は、長いです。
- 失敗は、全部捨てず、拾い直す。切れた JSON から、必要な部分だけ拾えます。
- AI は、指示に忠実。指示が、現実の書き方に合っていないと、正しく拒否します。
- 外の API の癖は、実測で切り分ける。ハイフンの前後で、400 と 200 が分かれました。
- 同じ癖は、同じ API を使う、ほかの場所にもある。楽天ブックスでも起きました。
この話に出てくるもの
- トレカダッシュ — トレカの抽選・予約・先着の情報を集める iPhone アプリ
- 本物の抽選を、静かに捨てていた 4 つの実例 — 別の「静かに失敗」
- Gemini の費用を削る前に、計器を疑った — AI の利用に関する別の記録
- 「取れた」と読んでいたのは、ただの案内ページだった — エラーが出ない失敗
ほかの記録
- 「iCloud が新しければ取り込む」の関数が、何も比べていなかった:応募の記録が 20 件から 1 件になりうる — iCloud 同期の関数は、名前に反して新しいかを見ず、変更があると丸ごと置き換えていた。新しい端末の空の状態で保存すると、別の端末の 20 件が 1 件に上書きされうる。更新時刻を比べ、空では上書きしないようにした。
- 「後から掃除するから大丈夫」が、入口の不具合を隠していた:商品が毎日 400 件そっくり作り直されていた — カタログからの商品登録が名前の完全一致でしか探さず、括弧・空白・全角の違いで毎日そっくりな商品を作り直し、この日だけで 437 件(410 件は記事なし)。統合の掃除が後から片付けていたため気づけなかった。
- Cloud Functions から月 4,300 円のサーバーへ移した:1 日 3,700 円の課金、EEA で全滅した Yahoo!、移行で踏んだ 5 つの罠 — 無料のお試しが終わると 1 日約 3,700 円(月約 10.6 万円)かかった。待ち時間にも課金される構造が原因で、コードをほぼ書き換えずにシンガポールの VPS(月約 4,300 円)へ移した。ヘルシンキでは Yahoo! が全滅、秘密の値は 5 本でなく 15 本だった。
コメント
感想や、記事の間違いの指摘は、ここへ。書くには、GitHub のアカウントが要ります。
押すと、giscus(giscus.app)と GitHub に接続します。押さなければ、接続しません。詳しくはあつかいに書いています。