[jupyter / Python] nbconvert 実行時に "state" が無いというエラーが出る時の対策

# 1. 概要

`ipynb` ファイルを HTML ファイルに変換しようとした時にハマったエラーの対策を記載しています。

# 2. 症状

## 2-1. Jupyter の WEB画面上から変換を試みた場合

ここで HTML 化できるはずだが、

![スクリーンショット 2023-09-27 8.39.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327407/a4be0450-6a3b-f78e-41f0-f7e2c0bc786f.png)

以下のように、エラー表示となる。

![スクリーンショット 2023-09-27 8.39.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327407/194c71f8-abbe-5ae5-2836-e7c5d2dd854a.png)

エラー文言

```
500 : Internal Server Error
The error was:

nbconvert failed: 'state'
```

## 2-2. コマンドで変換を試みた場合

```bash
$ jupyter nbconvert --to html 23mmdd_sample.ipynb
```

以下のようなエラーが発生した。（長いので畳んであります）

<details><summary>エラー内容を確認する</summary>

```bash
[NbConvertApp] Converting notebook 23mmdd_sample.ipynb to html
Traceback (most recent call last):
  File "/usr/local/bin/jupyter-nbconvert", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.9/site-packages/jupyter_core/application.py", line 285, in launch_instance
    return super().launch_instance(argv=argv, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/traitlets/config/application.py", line 1046, in launch_instance
    app.start()
  File "/usr/local/lib/python3.9/site-packages/nbconvert/nbconvertapp.py", line 410, in start
    self.convert_notebooks()
  File "/usr/local/lib/python3.9/site-packages/nbconvert/nbconvertapp.py", line 585, in convert_notebooks
    self.convert_single_notebook(notebook_filename)
  File "/usr/local/lib/python3.9/site-packages/nbconvert/nbconvertapp.py", line 551, in convert_single_notebook
    output, resources = self.export_single_notebook(
  File "/usr/local/lib/python3.9/site-packages/nbconvert/nbconvertapp.py", line 477, in export_single_notebook
    output, resources = self.exporter.from_filename(
  File "/usr/local/lib/python3.9/site-packages/nbconvert/exporters/templateexporter.py", line 389, in from_filename
    return super().from_filename(filename, resources, **kw)  # type:ignore
  File "/usr/local/lib/python3.9/site-packages/nbconvert/exporters/exporter.py", line 201, in from_filename
    return self.from_file(f, resources=resources, **kw)
  File "/usr/local/lib/python3.9/site-packages/nbconvert/exporters/templateexporter.py", line 395, in from_file
    return super().from_file(file_stream, resources, **kw)  # type:ignore
  File "/usr/local/lib/python3.9/site-packages/nbconvert/exporters/exporter.py", line 220, in from_file
    return self.from_notebook_node(
  File "/usr/local/lib/python3.9/site-packages/nbconvert/exporters/html.py", line 260, in from_notebook_node
    html, resources = super().from_notebook_node(nb, resources, **kw)
  File "/usr/local/lib/python3.9/site-packages/nbconvert/exporters/templateexporter.py", line 427, in from_notebook_node
    output = self.template.render(nb=nb_copy, resources=resources)
  File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 1301, in render
    self.environment.handle_exception()
  File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "/usr/local/share/jupyter/nbconvert/templates/lab/index.html.j2", line 4, in top-level template code
    {% from 'jupyter_widgets.html.j2' import jupyter_widgets %}
  File "/usr/local/share/jupyter/nbconvert/templates/lab/base.html.j2", line 3, in top-level template code
    {% from 'cell_id_anchor.j2' import cell_id_anchor %}
  File "/usr/local/share/jupyter/nbconvert/templates/base/display_priority.j2", line 1, in top-level template code
    {%- extends 'base/null.j2' -%}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 26, in top-level template code
    {%- block body -%}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 29, in block 'body'
    {%- block body_loop -%}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 31, in block 'body_loop'
    {%- block any_cell scoped -%}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 34, in block 'any_cell'
    {%- block codecell scoped -%}
  File "/usr/local/share/jupyter/nbconvert/templates/lab/base.html.j2", line 13, in block 'codecell'
    {{ super() }}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 44, in block 'codecell'
    {%- block output_group -%}
  File "/usr/local/share/jupyter/nbconvert/templates/lab/base.html.j2", line 39, in block 'output_group'
    {{ super() }}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 48, in block 'output_group'
    {%- block outputs scoped -%}
  File "/usr/local/share/jupyter/nbconvert/templates/lab/base.html.j2", line 45, in block 'outputs'
    {{ super() }}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 50, in block 'outputs'
    {%- block output scoped -%}
  File "/usr/local/share/jupyter/nbconvert/templates/lab/base.html.j2", line 92, in block 'output'
    {{ super() }}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 67, in block 'output'
    {%- block display_data scoped -%}
  File "/usr/local/share/jupyter/nbconvert/templates/base/null.j2", line 68, in block 'display_data'
    {%- block data_priority scoped -%}
  File "/usr/local/share/jupyter/nbconvert/templates/lab/base.html.j2", line 131, in block 'data_priority'
    {{ super() }}
  File "/usr/local/share/jupyter/nbconvert/templates/base/display_priority.j2", line 7, in block 'data_priority'
    {%- for type in output.data | filter_data_type -%}
  File "/usr/local/lib/python3.9/site-packages/nbconvert/filters/widgetsdatatypefilter.py", line 58, in __call__
    metadata["widgets"][WIDGET_STATE_MIMETYPE]["state"]
KeyError: 'state'
```

</details>

# 3. 対策

[GitHub issueにある `@ndjhartman` さんのコメント](https://github.com/jupyter/nbconvert/issues/1731#issuecomment-1491048550)で対処できた。

> Dumb workaround for without access to CLI jq and without reuploading to Colab:
> 
> 1. Open .ipynb file in notepad
> 2. Copy file JSON contents
> 2. Go to https://jqplay.org/
> 2. Paste contents into JSON field
> 2. Copy/paste del(.metadata.widgets) into filter pane (Thanks @hs211216)
> 2. Copy result from right Result field
> 2. Paste into original .ipynb file
> 2. Save file and exit
> 
> Works now.


## 具体的な手順



以下のような画面が表示されるので、 `JSON` という欄に先ほどコピーしたデータを貼り付ける。

![スクリーンショット 2023-09-27 8.46.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327407/f481446d-8ff6-3bdf-c245-4d0d2f508f76.png)


次は、 `Filter` 欄に、以下のテキストをコピー＆ペーストする。

`del(.metadata.widgets)`

すると、 `Reuslt` 欄に `Loading...` の文言が現れ...

![スクリーンショット 2023-09-27 8.46.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327407/8328c1f7-9e16-2682-99d2-9fc6b5d2e322.png)

最終的には、以下のように変換結果が出力される。

![スクリーンショット 2023-09-27 8.47.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327407/6ada121b-1bd4-be6a-0441-5eb361cf3b38.png)

これを、元に新たな ipynb ファイルを作成する。

その ipynb ファイルを `nbconvert` で以下のように変換すれば、成功する。

```bash
root@49944be57f22:/workspace/notebooks# jupyter nbconvert --to html 23mmdd_sample.ipynb 
[NbConvertApp] Converting notebook 23mmdd_sample.ipynb to html
[NbConvertApp] WARNING | Alternative text is missing on 21 image(s).
[NbConvertApp] Writing 1139653 bytes to 23mmdd_sample.html
```


# 4. 参考記事

## コマンド実行する場合の手順

https://qiita.com/yorshka/items/80a0352b745d5c10282c

## 今回の対策が書かれていた GitHub issue

https://github.com/jupyter/nbconvert/issues/1731#issuecomment-1491048550

## 利用したWEBサービス

https://jqplay.org/