# 通知設計書 37-動的メタデータエラー通知

## 概要

本ドキュメントは、Next.js開発サーバーにおける動的メタデータエラー通知の設計について記載する。`generateMetadata()`内で動的データにアクセスした場合にオーバーレイで修正方法を案内する通知機能である。

### 本通知の処理概要

動的メタデータエラー通知は、App Routerの`generateMetadata()`関数内でランタイムデータやナビゲーションブロッキングデータにアクセスした場合に、ページが意図せず部分的に動的になることを防ぐために、開発者に修正方法を案内するオーバーレイを表示する機能である。

**業務上の目的・背景**：Next.jsのApp Routerでは、ドキュメントメタデータ（`generateMetadata()`）は通常プリレンダリング可能であるべきである。メタデータのみが動的な場合、ページ全体が意図せず非プリレンダリング対象になってしまう。本通知は、開発者がこの問題に早期に気づき、メタデータをプリレンダリング可能にするか、ページの他の部分も明示的に動的にするかを選択できるようにする。

**通知の送信タイミング**：ランタイムエラーのメッセージに`/next-prerender-dynamic-metadata`が含まれる場合に検出される。`getBlockingRouteErrorDetails()`関数でエラーメッセージのパターンマッチにより判定される。

**通知の受信者**：開発中のアプリケーションをブラウザで表示している開発者。

**通知内容の概要**：エラー種別ラベル「Ambiguous Metadata」、問題の説明（generateMetadata()内での動的データアクセス）、修正方法（`"use cache"`への移動、`connection()`と`<Suspense>`の追加等）、関連ドキュメントリンク（https://nextjs.org/docs/messages/next-prerender-dynamic-metadata）が表示される。

**期待されるアクション**：開発者は提案された修正方法のいずれかを適用する。具体的には、非同期処理を`"use cache"`コンポーネントに移動するか、ページのどこかに`connection()`を`<Suspense>`内で使用してページが動的であることを明示する。

## 通知種別

ブラウザUI / Dev Overlay（ランタイムエラーオーバーレイの特殊表示）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（ランタイムエラーとして捕捉後、動的メタデータエラー判定で特殊表示） |
| 優先度 | 高 |
| リトライ | 無し |

### 送信先決定ロジック

ランタイムエラーオーバーレイの一部として動作する。`getBlockingRouteErrorDetails()`関数がエラーメッセージに`/next-prerender-dynamic-metadata`パターンが含まれるかを判定し、一致する場合に`DynamicMetadataErrorDetails`を返す。`cookies()`の存在により`runtime`または`navigation`のバリアントを判定する。

## 通知テンプレート

### ブラウザUI表示の場合

| 項目 | 内容 |
|-----|------|
| ヘッダーラベル | Ambiguous Metadata |
| 問題の説明 | バリアント（runtime/navigation）に応じた説明 |
| 修正方法 | 2つの修正オプション |
| ドキュメントリンク | https://nextjs.org/docs/messages/next-prerender-dynamic-metadata |
| 形式 | HTMLオーバーレイ |

### 本文テンプレート（variant=navigationの場合）

```
[Ambiguous Metadata]

Data that blocks navigation was accessed inside generateMetadata()
in an otherwise prerenderable page

When Document metadata is the only part of a page that cannot be
prerendered Next.js expects you to either make it prerenderable or
make some other part of the page non-prerenderable to avoid
unintentional partially dynamic pages.

To fix this:
  Option 1: Move the asynchronous await into a Cache Component ("use cache").
  Option 2: Add connection() inside a <Suspense> somewhere in a Page or Layout.

Learn more: https://nextjs.org/docs/messages/next-prerender-dynamic-metadata
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| variant | エラーバリアント | 'runtime' or 'navigation'（cookies()の有無で判定） | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ランタイムエラー | generateMetadata内でのデータアクセス | error.message.includes('/next-prerender-dynamic-metadata') | generateMetadata()内で動的データにアクセスした場合 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| エラーメッセージに該当パターンが含まれない | 動的メタデータエラーでない場合は他のエラー表示が使用される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ランタイムエラー発生] --> B[useErrorDetails呼び出し]
    B --> C[getHydrationErrorDetails判定]
    C --> D{ハイドレーションエラー?}
    D -->|Yes| E[ハイドレーションエラー表示]
    D -->|No| F[getBlockingRouteErrorDetails呼び出し]
    F --> G{/blocking-route含む?}
    G -->|Yes| H[ブロッキングルートエラー表示]
    G -->|No| I{/next-prerender-dynamic-metadata含む?}
    I -->|Yes| J{cookies()含む?}
    J -->|Yes| K[variant=runtime]
    J -->|No| L[variant=navigation]
    K --> M[DynamicMetadataErrorDescription表示]
    L --> M
    I -->|No| N[次の判定へ]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（データベースは使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| パターン判定失敗 | エラーメッセージのURLパターンが変更された場合 | 通常のランタイムエラーとして表示される（フォールバック） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（開発サーバー稼働中は常時表示可能）

## セキュリティ考慮事項

- エラーメッセージには開発者向けの技術的な情報のみが含まれ、機密データは含まれない
- Dev Overlayは本番ビルドには含まれない

## 備考

- `DynamicMetadataErrorDescription`コンポーネントは2つのバリアント（navigation/runtime）に応じた異なる説明を表示する：
  - variant=navigation：ナビゲーションブロッキングデータ（キャッシュされていないfetch、connection()等）へのアクセス
  - variant=runtime：ランタイムデータ（cookies()、headers()等）へのアクセス。ファイルベースのメタデータ使用時の注意事項も含む
- エラー種別ラベルは`getErrorTypeLabel()`で「Ambiguous Metadata」に設定される（errors.tsx 443-445行目）
- `BlockingPageLoadErrorDescription`コンポーネント内にも`refinement === 'generateMetadata'`の場合の説明が含まれるが、これは`getBlockingRouteErrorDetails()`の判定順序により`DynamicMetadataErrorDetails`として処理される

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、動的メタデータエラーの型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | errors.tsx | `packages/next/src/next-devtools/dev-overlay/container/errors.tsx` | `DynamicMetadataErrorDetails`型（478-481行目）。`type: 'dynamic-metadata'`, `variant: 'navigation' | 'runtime'`の2フィールド |

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | errors.tsx | `packages/next/src/next-devtools/dev-overlay/container/errors.tsx` | `getBlockingRouteErrorDetails()`（545-582行目）。558-567行目の`/next-prerender-dynamic-metadata`判定ロジック |

**主要処理フロー**:
1. **558行目**: `error.message.includes('/next-prerender-dynamic-metadata')`チェック
2. **562行目**: `cookies()`の有無でvariant判定
3. **563-566行目**: `DynamicMetadataErrorDetails`返却

#### Step 3: 表示コンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | errors.tsx | `packages/next/src/next-devtools/dev-overlay/container/errors.tsx` | `DynamicMetadataErrorDescription`コンポーネント（65-160行目）。navigationとruntimeの2パターンの説明 |

**主要処理フロー**:
- **70行目**: `variant === 'navigation'`の場合の分岐
- **74-75行目**: 「Data that blocks navigation was accessed inside generateMetadata()」の説明
- **114行目**: `variant === 'runtime'`の場合（else分岐）
- **118-119行目**: 「Runtime data was accessed inside generateMetadata() or file-based metadata」の説明

### プログラム呼び出し階層図

```
Errors [errors.tsx:584]
    |
    +-- useErrorDetails() [errors.tsx:487]
    |      +-- getBlockingRouteErrorDetails() [errors.tsx:545]
    |             +-- /next-prerender-dynamic-metadata判定 [errors.tsx:558]
    |
    +-- getErrorTypeLabel() [errors.tsx:435]
    |      +-- "Ambiguous Metadata" 返却 [errors.tsx:443]
    |
    +-- DynamicMetadataErrorDescription [errors.tsx:65]
           +-- variant=navigation: ナビゲーションブロッキング説明
           +-- variant=runtime: ランタイムデータアクセス説明
```

### データフロー図

```
[入力]                                    [処理]                              [出力]

ランタイムエラー                      getBlockingRouteErrorDetails()     ブラウザ
(Error with                   ------> DynamicMetadataErrorDetails  ----> オーバーレイ
 /next-prerender-dynamic-             variant判定                        (問題説明 +
  metadata)                           DynamicMetadataError               修正方法 +
                                      Description表示                     ドキュメントリンク)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| errors.tsx | `packages/next/src/next-devtools/dev-overlay/container/errors.tsx` | ソース | DynamicMetadataErrorDetails型定義、getBlockingRouteErrorDetails判定、DynamicMetadataErrorDescription表示 |
