> ## 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.

# Solução de problemas

> Diagnostique problemas de criptografia no X Chat: erros do Chat XDK, recuperação de backup de chaves, falhas de descriptografia e payloads assinados.

Esta página cobre problemas que são **específicos da criptografia do X Chat e do Chat XDK** — chaves, backup seguro de chaves, descriptografia/verificação e construção de payloads criptografados de envio.

Para webhooks, OAuth, códigos de status HTTP e limites de taxa, use a documentação geral da [X API](/x-api/introduction) e de [autenticação](/fundamentals/authentication/overview).

***

## Chaves e backup seguro de chaves

### O desbloqueio falha (código de acesso inválido)

* Confirme que o código de acesso corresponde ao usado com `setup`
* Aguarde entre tentativas; realms limitam tentativas incorretas e podem travar a recuperação após muitas falhas

<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>

### Criptografar ou descriptografar falha porque as chaves ou a identidade não foram definidas

Carregue as chaves privadas primeiro e depois defina a **identidade da sessão** — seu ID de usuário mais o `public_key_version` do seu registro no X. Os métodos `encrypt_*` e `prepare_*` assinam com ela; chamá-los sem identidade da sessão (e sem uma sobrescrita explícita por chamada) é um erro.

<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>

### Sua chave pública local nunca corresponde às chaves registradas da conta

Os clientes frequentemente precisam responder *"a chave neste dispositivo é uma das chaves registradas para esta conta?"* — após uma restauração ou importação, para adotar o `public_key_version` correto, ou para decidir se o onboarding já aconteceu. Comparar a saída de `get_public_keys` do Chat XDK com o campo `public_key` da API **como strings sempre falha**, mesmo para a mesma chave, porque as duas usam codificações diferentes:

* A **API** armazena e retorna a chave exatamente como o registro fez o upload: a codificação DER (SPKI) — a chave crua atrás de um prefixo fixo de identificador de algoritmo
* O `get_public_keys` do **Chat XDK** retorna apenas a chave crua, sem esse prefixo

A mesma chave, duas grafias. Para comparar, decodifique ambas de base64 e verifique se os bytes da API **terminam com** os bytes do SDK (bytes idênticos também correspondem, caso ambos os lados algum dia mantenham a mesma codificação):

<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>

Depois de haver correspondência, adote o `public_key_version` daquela linha para `set_identity`. Ao comparar versões (por exemplo, para escolher a chave mais nova), compare **numericamente** — as versões são timestamps em milissegundos com comprimento de string variável, então uma comparação lexicográfica escolhe a errada.

### Chave de conversa ausente para uma mensagem

Um erro como `Message encrypted with key version '…' but no matching key found` significa que você não tem a chave **em bruto** para o `conversation_key_version` daquela mensagem.

1. Descriptografe o material de chave a partir de `conversation_key_change_event` (eventos ao vivo) ou `meta.conversation_key_events` (histórico) com `extract_conversation_keys`, **ou** inclua esses blobs em `decrypt_events` — com `set_cache_keys(true)` habilitado, `decrypt_events` também retém a chave verificada mais recente de cada conversa para que chamadas posteriores de `decrypt_event` e `encrypt_*` possam omiti-la
2. Confirme que as chaves de conversa foram adicionadas para essa versão e que você ainda é um participante (veja [Guia de introdução](/xchat/getting-started#4-set-up-conversation-keys))

### O par não tem chaves públicas

Pode ser que ele não tenha concluído o onboarding. Depois que ele se registrar, carregue `public_key`, `signing_public_key`, `identity_public_key_signature` e `public_key_version` a partir de **Referência da API → Chaves de criptografia**.

***

## Descriptografia e assinaturas

### A descriptografia falha

* Chave de conversa **em bruto** obsoleta ou errada, ou versão de chave errada
* String `encoded_event` incompleta
* O tipo de evento não é uma mensagem criptografada que você pode tratar como conteúdo descriptografável

### A assinatura não verifica

A verificação é **fail-closed por padrão** (`reject_unverified = true`): o SDK já rejeita eventos assinados não verificados, então uma falha aqui significa que as entradas de verificação estão erradas, e não que você precisa ativar a verificação. Causas comuns:

* Entrada de chave de assinatura ausente ou incompleta para o **remetente** (todos os campos exigidos pelo Chat XDK — veja a referência do [Chat XDK](/xchat/xchat-xdk))
* Nenhuma chave de assinatura passada na chamada e nenhuma armazenada via `set_signing_keys`
* O remetente rotacionou versões — busque as chaves públicas dele novamente
* Uma versão de chave abaixo do piso aceito nunca é verificada
* Em um **evento de mudança de chave de grupo**, quem assinou já saiu do grupo, então suas chaves não são mais servidas — veja [Mudanças de chave de membros que saíram](/xchat/groups#mudanças-de-chave-de-membros-que-saíram)

O setter `set_reject_unverified` existe para você **desabilitar** esse padrão (`false`, não recomendado). Se você o desativou antes, restaure o padrão 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>

### Uma resposta carrega `reply_preview_validation: "Invalid"`

Respostas descriptografadas podem carregar `reply_preview_validation` (`"Valid"` / `"Invalid"`; JavaScript usa `'valid'` / `'invalid'`). `Invalid` significa que a pré-visualização citada dentro da mensagem não corresponde ao evento original assinado que ela embute — trate a citação como não confiável e renderize o conteúdo citado apenas a partir do original validado. A mensagem em si é verificada separadamente e continua autêntica; nada é lançado por uma pré-visualização inválida.

### Eventos antigos falham permanentemente na verificação

Erros como `signature missing or no matching signing key` ou uma incompatibilidade ECDSA em eventos **antigos** são permanentes. Assinaturas são imutáveis e verificadas reconstruindo o payload assinado a partir do próprio evento, então um evento que foi assinado sobre bytes diferentes (ou nunca foi assinado) falha em cada carregamento futuro — nenhuma nova tentativa, atualização de chave ou chamada de API pode curá-lo. Trate esses eventos como lápides, não como erros repetíveis. Rotacionar a chave de conversa inicia um histórico limpo e verificável a partir daquele ponto; novas mensagens não são afetadas.

***

## Construindo o payload de envio

Estes erros são específicos da criptografia do X Chat (não são erros HTTP gerais):

| Problema                     | Correção                                                                                                                                                                                                                                                  |
| :--------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bytes de chave errados       | Passe os bytes da chave de conversa **em bruto** para o Chat XDK, não a string de chave criptografada da API                                                                                                                                              |
| Nomes de campos JSON errados | Mapeie `encrypted_content` → `encoded_message_create_event` e `encoded_event_signature` → `encoded_message_event_signature`                                                                                                                               |
| ID de mensagem errado        | Envie o `message_id` do payload retornado — o SDK o gera e o embute no evento assinado, então qualquer outro valor falha. Em reenvios, reutilize o mesmo payload criptografado para que o ID nunca seja cunhado duas vezes                                |
| Versão incompatível          | Alinhe `conversation_key_version` com a chave usada; alinhe a versão da chave de assinatura passada para `set_identity` com seu registro de chave pública                                                                                                 |
| Forma do ID no path          | Paths de URL ainda precisam do ID de conversa com hífen (`:` → `-`), mas para assinar o SDK aceita qualquer forma: `A:B`, `A-B` (em qualquer ordem), ou apenas o ID de usuário do destinatário — todos são canonicalizados para os mesmos bytes assinados |

### A API retorna 400 para uma chamada que altera estado

Toda chamada de chat que altera estado — adicionar ou rotacionar chaves de conversa, criar um grupo, adicionar membros — requer **`action_signatures`** no corpo da requisição, validado na fronteira da API. Uma entrada ausente ou malformada (cada uma precisa de `message_id`, `encoded_message_event_detail` e um `message_event_signature` com `signature`, `public_key_version` e `signature_version`) retorna imediatamente uma resposta HTTP 400 problem-details. Use os métodos prepare do SDK (`prepare_conversation_key_change`, `prepare_group_create`, `prepare_group_members_change`) e envie **todas** as assinaturas retornadas — criar grupo e adicionar membros retornam duas.

***

## Criptografar e descriptografar mídia

* Use a **mesma** chave de conversa (e versão) da mensagem que referencia o anexo
* Trate respostas de download como **texto cifrado** até executar `decrypt_stream`
* Deduza o tipo MIME **após** descriptografar; o `Content-Type` do download frequentemente não é o tipo real da imagem

Detalhes: [Mídia](/xchat/media).

***

## Depuração segura

Ao investigar falhas de criptografia:

* Registre apenas IDs de conversa, IDs de evento e **versões** de chave
* **Não** registre texto simples, códigos de acesso, chaves privadas ou blobs de chave completos
* Confirme que a versão de chave de assinatura passada para `set_identity` corresponde ao `public_key_version` no seu registro de chave pública
* Para histórico incompleto, pagine **todas** as páginas de eventos para que os metadados de mudança de chave não sejam pulados antes de descriptografar
