Claude Codeでpapermillのノートブック自動実行を直す — 失敗セルだけ渡す手順
papermillでパラメータ実行したノートブックが途中で止まったとき、出力ノートブックから失敗セルだけをjqで抜き、claude -pに原因を読ませるバッチの組み方をまとめます。
月次レポートのノートブックを、毎月手で開いて日付を書き換えて実行している。そこをpapermillで「パラメータを渡して実行するバッチ」に直し、途中で落ちたらClaude Codeのclaude -pに失敗セルだけを読ませる。この記事では、その流れを手元で動かした出力つきで組み立てます。
先に結論を書くと、claude -pに渡すのは出力ノートブック全体ではなく、失敗したセルのソースとエラーだけです。papermillは失敗しても出力ノートブックを残すので、そこからjqで数百バイトの断片を抜けます。
papermillは何をするツールか
papermillは、Jupyterノートブックにパラメータを渡して実行するためのツールです。公式の説明では、できることは「ノートブックのパラメータ化」と「実行」の2つです。月初と年初で値を変えて同じレポートを回す、といった使い方が例に挙がっています。
バッチ化の前提になるのは、ノートブックの側に「ここが差し替え可能な値」と示すことです。次の節で、その印の付け方を見ます。
差し替える値をparametersセルに切り出す
パラメータにしたい変数を1つのセルにまとめ、そのセルにparametersというタグを付けます。タグは、Jupyter Notebookならセルツールバーのタグ、JupyterLab 3.0以降ならプロパティインスペクタ(右サイドバー)から付けます。セルのメタデータに"tags": ["parameters"]を直接書いても同じです。
# tags: parameters
month = "2026-09"
source_csv = "sales.csv"このセルの値は「既定値」として扱われます。実行時に-pで渡した値だけが、injected-parametersというタグ付きの新しいセルに入り、parametersセルの直後に挿入されます。parametersタグのセルが無いノートブックでは、injected-parametersセルが先頭に入ります。
派生値は再計算されない
ここに1つ罠があります。parametersセルの中で、別のパラメータから計算した値を作ってはいけません。
公式の例では、a = 1とtwice = a * 2を同じセルに書き、-p a 9で実行するとtwiceは18にならず2のままです。上書きされるのはinjected-parametersセルに入った値だけで、元のセルの計算式は既定値のまま先に走るからです。
派生値はparametersセルの外、次のセル以降で計算します。月から集計期間を作るような処理をparametersセルに書いていると、値が食い違った出力が成功扱いで残ります。失敗にならないぶん、原因調査の対象にもなりません。
CLIで月ごとに実行する
実行はpapermill 入力 出力 -p 名前 値の形です。数値や真偽値に見える値は、そのまま数値・真偽値として解釈されます。文字列のまま渡したいときは-r(--parameters_raw)を使います。値が多いときはYAMLファイルを-fで渡せます。
mkdir -p out
papermill report.ipynb out/report-2026-10.ipynb \
-p month 2026-10 \
-p source_csv sales_2026_10.csv \
--execution-timeout 600--execution-timeoutは、1セルあたりの待ち時間を秒で指定するオプションです。既定は「永遠に待つ」なので、スケジュール実行に入れるなら入れておくほうが安全です。カーネルは-kで指定でき、NoSuchKernel系のエラーが出る場合の対処は後半の「つまずき」で扱います。
出力先のディレクトリは、papermillが作ってくれません。手元のpapermill 2.7.0(Python 3.12)でout/を作らずに実行したところ、実行の最初の保存でFileNotFoundError: output folder out doesn't exist.が出て止まりました。上のコマンドの先頭にmkdir -p outがあるのはそのためです。
失敗したとき、出力ノートブックに何が残るか
存在しないCSVを指定した例で、わざと失敗させました。papermillは終了コード1で止まり、標準エラーにはPapermillExecutionErrorと、失敗したセルの入力番号、FileNotFoundErrorのトレースバックが出ます。ここで重要なのは、出力ノートブックが書き出されている点です。
出力ノートブックの各セルをjqで確かめると、次のようになっていました(papermill 2.7.0での観察です)。
| セル(1始まり) | 内容 | metadata.papermill.status |
|---|---|---|
| 1 | 内容parametersセル(既定値) | metadata.papermill.statuscompleted |
| 2 | 内容injected-parametersセル(month = "2026-10"など) | metadata.papermill.statuscompleted |
| 3 | 内容先頭の通常セル(print) | metadata.papermill.statuscompleted |
| 4 | 内容papermillが差し込んだ赤字の説明(Markdown) | metadata.papermill.statusなし |
| 5 | 内容失敗したセル。出力にoutput_type: error | metadata.papermill.statusfailed |
| 6 | 内容以降のセル | metadata.papermill.statuspending |
失敗したセルの出力は、ノートブック形式のerror出力です。形式の仕様では、ename(例外名)、evalue(例外の値)、traceback(フレームを文字列にしたリスト)を持ちます。papermillはさらに、失敗の直前に「ここで例外が起きて止まった」という赤字のMarkdownセルを差し込みます。
つまり、原因調査に必要な情報は次の3つに集約されます。
- 実際に渡った値(
injected-parametersセル) - 失敗したセルのソース
ename・evalue・traceback
今回の出力ノートブックは5.5KB程度でしたが、図や表の出力を含むレポートでは大きくなります。ここを丸ごと読ませる必要はありません。
失敗セルだけを抜くjqスクリプト
Claude CodeのReadツールは、.ipynbを全セルと出力つきで返します。ただし、セルの合計が256KBを超えるか、トークン上限を超えると、内容ではなくエラーを返します。標準入力に流し込む場合も、上限は10MBです。出力つきのノートブックは、この両方に当たりやすい形式です。
そこで、jqで失敗セルを抜いてから渡します。次のスクリプトをextract_failure.shとして置きます。
#!/usr/bin/env bash
# 使い方: extract_failure.sh out/report-2026-10.ipynb
jq '{
parameters: (.cells[]
| select(.metadata.tags // [] | index("injected-parameters"))
| .source | join("")),
failed: [ .cells | to_entries[]
| select(.value.metadata.papermill.status == "failed")
| { cell: .key,
source: (.value.source | join("")),
error: [ .value.outputs[]
| select(.output_type == "error")
| { ename, evalue,
traceback: (.traceback
| map(gsub("\u001b\\[[0-9;]*m"; ""))) } ] } ]
}' "$1"トレースバックには色付けのエスケープシーケンスが入っているので、gsubで取り除いています。status == "failed"で絞れるのは、papermillが各セルのmetadata.papermillに実行状態を書くためです。先ほどの失敗例にかけると、出力は816バイトでした。
{
"parameters": "# Parameters\nmonth = \"2026-10\"\nsource_csv = \"sales_2026_10.csv\"\n",
"failed": [
{
"cell": 4,
"source": "rows = list(csv.DictReader(open(source_csv)))\nprint(len(rows))",
"error": [
{
"ename": "FileNotFoundError",
"evalue": "[Errno 2] No such file or directory: 'sales_2026_10.csv'",
"traceback": ["…(省略)…"]
}
]
}
]
}cellの値は0始まりの配列位置なので、上の表で5番目の失敗セルは4になります。papermillが差し込むMarkdownセルの分だけ、エディタで見える番号とはずれます。セルの特定にはソースが入っているので、番号は補助情報として扱います。
claude -pに原因を読ませる
抜いた断片をパイプで渡します。claude -pは標準入力を読めるので、ファイルの読み取り権限を与えなくても動きます。公式のbuild scriptの例も、差分をパイプで渡してClaudeにBashの許可を求めさせない形です。
./extract_failure.sh out/report-2026-10.ipynb | claude -p \
"papermillで実行したノートブックの失敗セルと実際に渡ったパラメータです。\
原因を1つに絞り、根拠になる行と、直す場所(パラメータ・データ・コードのどれか)を答えて。\
推測が混じる場合は推測と書いて。"出力はテキストで返り、実行が成功すれば終了コードは0です。結果をレポートに貼りたいなら、--output-format jsonを付けるとresultフィールドに本文が入り、jq -r '.result'で取り出せます。
診断を構造化して返させる
原因の分類をあとで集計したいときは、--json-schemaで形を決められます。回答はstructured_outputフィールドに入ります。
./extract_failure.sh out/report-2026-10.ipynb | claude -p \
"失敗の原因を分類して" \
--output-format json \
--json-schema '{"type":"object","properties":{"kind":{"type":"string","enum":["parameter","data","code","environment"]},"evidence":{"type":"string"},"fix":{"type":"string"}},"required":["kind","evidence","fix"]}' \
| jq '.structured_output'kindの値は、この記事で想定した4分類(パラメータ・データ・コード・環境)の例です。papermillやClaude Codeの用語ではありません。なお、Claudeが依頼を断るなどして有効な構造化出力が得られなかったときはstructured_outputが無いため、スクリプトでは存在を確かめてから使います。
ここで返ってくる回答の中身は、実行のたびに変わりえます。FileNotFoundErrorの例なら、「sales_2026_10.csvが作業ディレクトリに無い。月のパラメータとファイル名の組み立てを確認する」のように原因が1つに絞れれば成功です。最後の判断は、Claudeの答えではなく、人がevidenceを見て行います。
実行から診断までを1本のバッチにする
ここまでをrun_report.shにまとめます。papermillの終了コードで分岐し、失敗したときだけ診断を走らせます。
#!/usr/bin/env bash
set -u
month="$1" # 例: 2026-10
out="out/report-${month}.ipynb"
mkdir -p out
if papermill report.ipynb "$out" \
-p month "$month" \
-p source_csv "sales_${month//-/_}.csv" \
--execution-timeout 600; then
exit 0
fi
./extract_failure.sh "$out" \
| claude --bare -p "papermillの失敗セルの原因を1つに絞って。根拠の行も示して。" \
> "out/report-${month}.diagnosis.txt"
exit 1--bareは、フック・プラグイン・MCPサーバー・自動メモリ・CLAUDE.mdの自動読み込みを飛ばして起動を速くするオプションです。サーバーや他人の環境でも同じ結果になるので、スクリプト向きとされています。ただし、サブスクリプションのログイン情報は使わないので、ANTHROPIC_API_KEYを環境変数に設定する必要があります。手元の対話環境のログインで動かすなら--bareは外します。
診断を走らせたあとも終了コードは1のままにしています。診断はあくまで補助で、バッチは失敗した、という事実をスケジューラ側に伝えるためです。
診断に権限を足すのはいつか
標準入力だけで足りるなら、権限は何も要りません。入力データの中身を見て判断させたい場合は、ファイルを読ませる必要があります。
claude -pでは、承認が要る呼び出しは、承認する仕組みが無いと拒否されます。v2.1.302以降は、権限モードを何も指定しないclaude -pがManualモードで始まります。読み取りだけを足すなら--allowedTools "Read"のように明示します。入力CSVの先頭数行を読ませたいが、書き込みや実行はさせたくない、という場合です。Bashまで許すかどうかは、バッチを動かす環境にどこまで触らせるかで決めてください。
ノートブックそのものを直させたい場合は、NotebookEditツールがセル単位で編集します。ただし、診断バッチの段階では直させず、原因の報告までに止めておくほうが、再実行の結果を人が確認できます。
よくある失敗と切り分け
診断を読む前に、papermill側の典型的な失敗を知っておくと、入力に何を渡すべきかが分かります。
落とし穴
NoSuchKernelでノートブックが1セルも動かない
condaの環境(nb_conda)でカーネルを指定したノートブックを、別の環境から実行するとNo such kernel named conda-env-…が出ます。papermillはこのメタデータから環境を判断できません。公式の対処は、対象の環境にjupyter(最低でもipykernel)を入れ、jupyter kernelspec install --user --name 名前でカーネルとして登録し、-k 名前で指定する流れです。この場合、失敗セルが存在しないので、extract_failure.shのfailedは空になります。標準エラーを診断に渡してください。
落とし穴
値が成功扱いで食い違う
前述のとおり、parametersセル内の派生値は-pの上書きに追随しません。例外は出ないので、診断バッチは動きません。出力の数値を見て人が気づく種類の不具合です。
落とし穴
出力先ディレクトリが無い
papermillは出力先のフォルダを作りません。実行の最初にFileNotFoundErrorで止まり、出力ノートブックも残らないため、failedセルは抽出できません。mkdir -pを忘れない、が唯一の対策です。
見分け方はこうです。標準エラーが出力先やカーネルの話なら実行の前段、PapermillExecutionErrorが出ていればセルの中の問題で、後者だけが出力ノートブックから診断できます。バッチでは両方を拾えるよう、標準エラーをファイルに残しておくと診断の入力を選べます。
長いログを渡したいとき
失敗セルのトレースバックは短くて済みますが、セルが大量のログをprintしている場合は話が変わります。標準入力は10MBが上限で、超えると明確なエラーと非ゼロの終了コードで終わります。ログが大きいときの絞り方は、claude -pにログを渡す際の10MB上限で扱っています。
同じ「失敗した成果物から原因の断片だけを抜いて渡す」型は、データ変換の分野にもあります。dbtのrun_results.jsonも、errorのノードだけを抜いて渡す構成です。非対話実行の権限や出力形式の全体像は、claude -pオプションの解説にまとまっています。
まとめ
papermillの強みは、失敗しても「どの値で、どのセルが、どのエラーで止まったか」が出力ノートブックに残る点です。それを丸ごとではなく、parametersの実値と失敗セルの3点だけに絞ってclaude -pへ渡すと、入力は数百バイトで済みます。
最初に整えるのは、parametersセルに派生値を置かないことと、mkdir -pと--execution-timeoutを入れたバッチの骨格です。診断は報告までにとどめ、直すかどうかは出力のevidenceを見てから決める運用が、無人実行では安全です。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。