Claude Media
Claude Codeでpapermillのノートブック自動実行を直す — 失敗セルだけ渡す手順

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: errormetadata.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を見てから決める運用が、無人実行では安全です。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/headless
  2. code.claude.com/docs/en/tools-reference
  3. papermill.readthedocs.io/en/latest/usage-parameterize.html
  4. papermill.readthedocs.io/en/latest/usage-execute.html
  5. nbformat.readthedocs.io/en/latest/format_description.html
この記事を共有:XはてブLinkedIn