はじめに
2026年9月23日、Notion APIとNotion MCPの「ページアイコン」まわりの仕様が変更されました。今回の変更は大きく3つです。
- 存在しないアイコン名・色を指定した
externalアイコンURLが、保存時にvalidation_errorで弾かれるようになった(breaking change) - 有効な組み込みアイコンURLを指定すると、ネイティブアイコン(
type: "icon")として保存されるようになった - Notion MCPで、ページ間のアイコンコピーがワンステップでできるようになった
一見地味な変更に見えますが、アイコンURLを動的に組み立ててpages.createやpages.updateを呼んでいる連携・スクリプトは、そのまま本番で失敗し始める可能性があります。特にNotion連携の自動化ツールやテンプレート生成スクリプトを運用している開発者は要注意です。
変更の全体像
今回の変更を、リクエストの処理フローとして整理すると以下のようになります。
読み書きの型が変わる(②)ことと、無効値がエラーになる(①)ことの両方が同時に起きている点がポイントです。
変更内容
① 存在しないアイコン名・色はvalidation_errorに(breaking change, severity: high)
https://www.notion.so/icons/<名前>_<色>.svg形式のURLで、Notion組み込みアイコンセットに実在しない名前や色を指定した場合の挙動が変わりました。
| 変更前 | 変更後 | |
|---|---|---|
| 対象 | icons/<存在しない名前>_<存在しない色>.svg |
同左 |
| 保存結果 | 成功(ただし壊れたアイコンとして保存) | 失敗 |
| レスポンス | 200 OK |
400 validation_error |
| 影響API | Create a page / Update page properties | 同左 |
⚠️ Breaking Change
これまで「保存はできるがアイコンが表示されない」という壊れた状態だったものが、明示的なエラーに変わりました。以前は成功していたリクエストが、今後は失敗するようになります。
📌 影響を受ける人
- アイコンURLをテンプレート文字列やユーザー入力から動的に組み立てて
pages.create/pages.updateを呼んでいる連携・Bot- Notion連携のテスト・シードスクリプトでダミーのアイコン名を使っている場合
- CIやバッチ処理でページを大量生成しており、エラーハンドリングが薄い実装
② 有効な組み込みアイコンURLはtype: "icon"で返る(severity: medium)
https://www.notion.so/icons/pizza_blue.svgのような実在する組み込みアイコンのURLをexternalとして渡した場合、Notion側でネイティブアイコンに変換して保存されるようになりました。書き込み時の型と読み出し時の型が一致しなくなる点に注意が必要です。
| 書き込み時(リクエスト) | 読み出し時(レスポンス) | |
|---|---|---|
| 変更前 | icon.type = "external" |
icon.type = "external" |
| 変更後 | icon.type = "external" |
icon.type = "icon" |
icon.type === "external"だけで分岐しているコードは、組み込みアイコンURLを渡したページを読み戻したときに分岐から漏れる可能性があります。
③ Notion MCPでページ間のアイコンコピーが可能に(severity: medium, 新機能)
Notion MCPのnotion-fetchが返すicons/pizza_blueのようなアイコン識別子を、そのままnotion-create-pages・notion-update-pageのicon引数に渡せるようになりました。
💡 Tips
テンプレートページのアイコンを複製先ページへコピーする、といった処理をMCP経由で組み立てているなら、変換や再構築なしにそのままicon引数へ渡せば動きます。
影響と対応
| 変更 | action_required | 対応内容 |
|---|---|---|
| ①validation_error化 | 要対応 | アイコンURL生成箇所に入力チェック・エラーハンドリングを追加 |
| ②type:"icon"化 | 要対応 |
icon.typeの分岐に"icon"を追加 |
| ③MCPコピー | 対応不要 | 既存コードへの影響なし、使うと便利 |
優先度が高いのは①と②です。 まずは自社のNotion連携コードで、アイコンURLを組み立てている箇所・icon.typeを判定している箇所をgrepなどで洗い出すことをおすすめします。
コード例
Before: エラーハンドリングなし・型分岐が不完全
// アイコンURLを動的に組み立てて保存
async function setPageIcon(pageId, iconName, iconColor) {
const url = `https://www.notion.so/icons/${iconName}_${iconColor}.svg`;
await notion.pages.update({
page_id: pageId,
icon: { type: "external", external: { url } },
});
}
// 読み出し時の型判定
function getIconUrl(page) {
if (page.icon.type === "external") {
return page.icon.external.url;
}
return null; // "icon" 型は考慮されていない
}
After: エラーハンドリングとtype: "icon"対応を追加
async function setPageIcon(pageId, iconName, iconColor) {
const url = `https://www.notion.so/icons/${iconName}_${iconColor}.svg`;
try {
await notion.pages.update({
page_id: pageId,
icon: { type: "external", external: { url } },
});
} catch (err) {
if (err.code === "validation_error") {
// 存在しないアイコン名・色が指定された
console.error(`無効なアイコン指定: ${iconName}_${iconColor}`, err);
// フォールバック処理やユーザーへの通知など
return;
}
throw err;
}
}
function getIconUrl(page) {
if (page.icon.type === "external") {
return page.icon.external.url;
}
if (page.icon.type === "icon") {
// 組み込みアイコンに変換された場合
return page.icon.icon?.id ?? null;
}
return null;
}
まとめ
- 存在しないNotion組み込みアイコン名・色を指定した
externalアイコンURLは、これまで壊れた状態のまま保存されていたが、validation_errorで明示的に弾かれるようになった - 有効な組み込みアイコンURLを指定すると、ネイティブアイコンとして保存され、読み出し時は
type: "icon"で返るようになった - Notion MCPでは
notion-fetchが返すアイコン識別子をnotion-create-pages/notion-update-pageにそのまま渡せるようになり、ページ間のアイコンコピーが簡単に行えるようになった
アイコンURLを動的に生成している連携や、icon.typeで分岐している既存コードは、本番影響が出る前に見直しておくことをおすすめします。