> ## Documentation Index
> Fetch the complete documentation index at: https://x-preview-mintlify-7f49d7a1.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# トラブルシューティング

> Chat XDK のエラー、安全な鍵バックアップからの復元、復号の失敗、署名付き送信ペイロードの構築など、X Chat の暗号化に関するよくある問題を診断します。

このページでは、**X Chat の暗号化と Chat XDK に特有の**問題（鍵、安全な鍵バックアップ、復号/検証、暗号化された送信ペイロードの構築）を扱います。

Webhook、OAuth、HTTP ステータスコード、レート制限については、一般的な [X API](/x-api/introduction) および [認証](/fundamentals/authentication/overview) のドキュメントを参照してください。

***

## 鍵と安全な鍵バックアップ

### アンロックに失敗する（無効なパスコード）

* パスコードが `setup` で使用したものと一致するか確認する
* 試行の間で待機する；realm は間違った推測をレート制限し、多くの失敗の後に復元をロックすることがある

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    try:
        chat.unlock(passcode)
    except ValueError as e:
        print(e)  # may mention InvalidPin or guesses remaining
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    try {
      await chat.unlock(passcode);
    } catch (e) {
      console.error((e as Error).message);
    }
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    chat.unlock(passcode_bytes).await?;
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    if err := chat.Unlock(passcode, juiceboxConfigJSON); err != nil {
        log.Println(err)
    }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    try { chat.Unlock(passcode, juiceboxConfigJson); }
    catch (Exception e) { Console.WriteLine(e.Message); }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    try { chat.unlock(passcode, juiceboxConfigJson); }
    catch (Exception e) { System.out.println(e.getMessage()); }
    ```
  </Tab>
</Tabs>

### 鍵や identity が未設定のために暗号化または復号に失敗する

まず秘密鍵をロードし、次に**セッション ID**を設定します — あなたのユーザー ID と X 上のレコードの `public_key_version` です。`encrypt_*` および `prepare_*` メソッドはこの ID で署名します；セッション ID が未設定（かつ呼び出しごとの明示的な上書きもない状態）でそれらを呼び出すとエラーになります。

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    chat.unlock(passcode)  # or: chat.import_keys(blob)
    chat.set_identity(my_user_id, signing_key_version)
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    await chat.unlock(passcode);
    chat.setIdentity(myUserId, signingKeyVersion);
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    chat.import_keys(&blob)?;
    chat.set_identity(&my_user_id, &signing_key_version);
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    blob, _ := chatxdk.Base64ToBytes(privateKeysB64)
    _ = chat.ImportKeys(blob)
    _ = chat.SetIdentity(myUserID, signingKeyVersion)
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    chat.ImportKeys(blobBytes);
    chat.SetIdentity(myUserId, signingKeyVersion);
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    chat.importKeys(blobBytes);
    chat.setIdentity(myUserId, signingKeyVersion);
    ```
  </Tab>
</Tabs>

### ローカルの公開鍵がアカウントの登録済み鍵と一致しない

クライアントはしばしば、*「このデバイス上の鍵は、このアカウントに登録されている鍵のいずれかか？」* に答える必要があります — 復元またはインポート後に正しい `public_key_version` を採用するため、あるいはオンボーディングがすでに完了しているかを判断するためです。Chat XDK の `get_public_keys` の出力を API の `public_key` フィールドと**文字列として比較すると、同じ鍵であっても常に失敗します**。両者が異なるエンコーディングを使用しているためです:

* **API** は登録時にアップロードされたとおりの鍵を保存して返します: DER (SPKI) エンコーディング — 固定のアルゴリズム識別子プレフィックスの後ろにある生の鍵
* **Chat XDK** の `get_public_keys` は、そのプレフィックスなしの生の鍵だけを返します

同じ鍵、2 つの表記です。比較するには、両方を base64 デコードし、API のバイト列が SDK のバイト列で**終わる**ことを確認します（両側が同じエンコーディングを持つ場合に備え、完全一致も一致として扱います）:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import base64

    def same_key(local_b64: str, server_b64: str) -> bool:
        local = base64.b64decode(local_b64)    # chat.get_public_keys()["identity"]
        server = base64.b64decode(server_b64)  # API row's "public_key"
        return local == server or (len(server) > len(local) and server.endswith(local))
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const sameKey = (localB64: string, serverB64: string): boolean => {
      const local = Buffer.from(localB64, 'base64');   // chat.getPublicKeys().identity
      const server = Buffer.from(serverB64, 'base64'); // API row's public_key
      return local.equals(server) ||
        (server.length > local.length && server.subarray(server.length - local.length).equals(local));
    };
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    fn same_key(local: &[u8], server: &[u8]) -> bool {
        local == server || (server.len() > local.len() && server.ends_with(local))
    }
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    func sameKey(local, server []byte) bool {
        return bytes.Equal(local, server) ||
            (len(server) > len(local) && bytes.HasSuffix(server, local))
    }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    static bool SameKey(byte[] local, byte[] server) =>
        local.SequenceEqual(server) ||
        (server.Length > local.Length &&
         server.AsSpan(server.Length - local.Length).SequenceEqual(local));
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    static boolean sameKey(byte[] local, byte[] server) {
        if (Arrays.equals(local, server)) return true;
        if (server.length <= local.length) return false;
        byte[] tail = Arrays.copyOfRange(server, server.length - local.length, server.length);
        return Arrays.equals(tail, local);
    }
    ```
  </Tab>
</Tabs>

一致したら、その行の `public_key_version` を `set_identity` に採用してください。バージョンを比較するとき（たとえば最新の鍵を選ぶ場合）は、**数値として**比較してください — バージョンはミリ秒のタイムスタンプで文字列長が可変なので、辞書順比較では誤ったものが選ばれます。

### メッセージに対する会話鍵が欠落している

`Message encrypted with key version '…' but no matching key found` のようなエラーは、そのメッセージの `conversation_key_version` に対応する**未加工の**鍵を持っていないことを意味します。

1. `extract_conversation_keys` で `conversation_key_change_event`（ライブイベント）または `meta.conversation_key_events`（履歴）から鍵素材を復号する、**または**これらの blob を `decrypt_events` に含める — `set_cache_keys(true)` が有効な場合、`decrypt_events` は各会話の最新の検証済み鍵も保持し、後続の `decrypt_event` と `encrypt_*` の呼び出しでは省略できます
2. そのバージョンの会話鍵が追加されていること、およびあなたがまだ参加者であることを確認する（[Getting Started](/xchat/getting-started#4-set-up-conversation-keys) を参照）

### ピアが公開鍵を持っていない

彼らはオンボーディングを完了していない可能性があります。彼らが登録した後、**API リファレンス → Encryption keys** から `public_key`、`signing_public_key`、`identity_public_key_signature`、`public_key_version` をロードしてください。

***

## 復号と署名

### 復号が失敗する

* 古いまたは間違った**未加工の**会話鍵、または間違った鍵バージョン
* 不完全な `encoded_event` 文字列
* イベントタイプが復号可能なコンテンツとして扱える暗号化メッセージではない

### 署名が検証されない

検証は**デフォルトで fail-closed**（`reject_unverified = true`）です: SDK はすでに検証されていない署名付きイベントを拒否しているため、ここでの失敗はチェックをオンにする必要があるという意味ではなく、検証入力が間違っていることを意味します。一般的な原因:

* **送信者**の署名鍵エントリが欠落または不完全（Chat XDK に必要なすべてのフィールド — [Chat XDK](/xchat/xchat-xdk) リファレンスを参照）
* 呼び出しに署名鍵が渡されておらず、`set_signing_keys` 経由で保存もされていない
* 送信者がバージョンをローテーションした — 公開鍵を再取得する
* 受け入れフロアより下の鍵バージョンは決して検証されない
* **グループの鍵変更イベント**で、署名者がすでにそのグループを離脱していると、その鍵はもはや配信されない — [離脱したメンバーによる鍵変更](/xchat/groups#離脱したメンバーによる鍵変更) を参照

`set_reject_unverified` セッターは、このデフォルトから**オプトアウト**する（`false`、推奨されません）ために存在します。以前に無効化した場合は、fail-closed のデフォルトを復元してください:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    chat.set_reject_unverified(True)
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    chat.setRejectUnverified(true);
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    chat.set_reject_unverified(true);
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    chat.SetRejectUnverified(true)
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    chat.SetRejectUnverified(true);
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    chat.setRejectUnverified(true);
    ```
  </Tab>
</Tabs>

### 返信の `reply_preview_validation` が `"Invalid"` になる

復号された返信には `reply_preview_validation`（`"Valid"` / `"Invalid"`；JavaScript は `'valid'` / `'invalid'` を使用）が付与される場合があります。`Invalid` は、メッセージ内の引用プレビューが埋め込まれた署名済みオリジナルイベントと一致しないことを意味します — 引用は信頼できないものとして扱い、引用コンテンツは検証済みのオリジナルからのみレンダリングしてください。メッセージ自体は別に検証されており依然として真正です；無効なプレビューによって例外がスローされることはありません。

### 古いイベントが恒久的に検証に失敗する

**古い**イベントでの `signature missing or no matching signing key` や ECDSA の不一致のようなエラーは恒久的です。署名は不変であり、イベント自体から署名済みペイロードを再構築することで検証されるため、異なるバイトに対して署名された（またはそもそも署名されていない）イベントは、将来のすべてのロードで失敗します — 再試行、鍵の更新、または API 呼び出しでは治せません。これらのイベントは、再試行可能なエラーではなくトゥームストーンとして扱ってください。会話鍵をローテーションすると、その時点からクリーンで検証可能な履歴が始まります；新しいメッセージには影響しません。

***

## 送信ペイロードの構築

これらのミスは X Chat の暗号化に特有のものです（一般的な HTTP エラーではありません）:

| 問題               | 修正                                                                                                                                |
| :--------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| 間違った鍵バイト         | API からの暗号化された鍵文字列ではなく、**未加工の**会話鍵バイトを Chat XDK に渡す                                                                                |
| 間違った JSON フィールド名 | `encrypted_content` → `encoded_message_create_event`、`encoded_event_signature` → `encoded_message_event_signature` にマップする         |
| 間違ったメッセージ ID     | 返却されたペイロードの `message_id` を送信する — SDK が生成し、署名付きイベントに埋め込むため、他の値はすべて失敗します。再試行時には同じ暗号化ペイロードを再利用して、ID が二度と生成されないようにする                  |
| バージョンの不一致        | `conversation_key_version` を使用する鍵と揃える；`set_identity` に渡す署名鍵バージョンを公開鍵レコードと揃える                                                      |
| パス ID の形式        | URL パスには依然としてハイフン付きの会話 ID が必要（`:` → `-`）ですが、署名の場合、SDK は任意の形式を受け付けます: `A:B`、`A-B`（どちらの順序でも）、または受信者のユーザー ID だけ — すべては同じ署名バイトに正規化される |

### 状態を変更する呼び出しに対して API が 400 を返す

状態を変更するすべてのチャット呼び出し — 会話鍵の追加やローテーション、グループの作成、メンバーの追加 — は、リクエストボディに **`action_signatures`** を要求し、API 境界で検証されます。エントリの欠落や不正な形式（それぞれに `message_id`、`encoded_message_event_detail`、および `signature`、`public_key_version`、`signature_version` を持つ `message_event_signature` が必要）は、即座に HTTP 400 problem-details レスポンスを返します。SDK の prepare メソッド（`prepare_conversation_key_change`、`prepare_group_create`、`prepare_group_members_change`）を使い、返された**すべての**署名を送信してください — グループ作成とメンバー追加は 2 つ返します。

***

## メディアの暗号化と復号

* 添付を参照するメッセージと**同じ**会話鍵（およびバージョン）を使用する
* ダウンロードレスポンスは `decrypt_stream` を実行するまで**暗号文**として扱う
* MIME タイプは復号**後**に推測する；ダウンロードの `Content-Type` は実際の画像タイプでないことがよくある

詳細: [Media](/xchat/media)。

***

## 安全なデバッグ

暗号処理の失敗を調査する際:

* 会話 ID、イベント ID、鍵の**バージョン**のみをログに記録する
* 平文、パスコード、秘密鍵、または完全な鍵 blob は**ログに記録しない**
* `set_identity` に渡した署名鍵バージョンが、公開鍵レコードの `public_key_version` と一致することを確認する
* 履歴が不完全な場合、復号する前に鍵変更のメタデータがスキップされないよう、**すべての**イベントページをページングする
