← Voltar ao blog

Erros comuns do Firebird (902, 10054, 104) e como resolver

Os códigos de erro mais comuns do Firebird, como 902, 10054, 104 e 335544569, com a causa real por trás de cada um e o que fazer para resolver.


O código de erro do Firebird é onde o diagnóstico costuma travar: 335544345 não diz nada e o log guarda só a primeira linha da mensagem. Quase todo erro em produção cai em quatro famílias — conexão, concorrência, dados e sintaxe — e cada família tem um caminho de investigação próprio.


O que significam os números de erro do Firebird?

O Firebird devolve sempre dois códigos diferentes para o mesmo problema: o SQLCODE (negativo, como -104) e o GDSCODE (um número longo, como 335544569). O texto da mensagem mostra os dois no final, e é por isso que a mesma falha aparece com números diferentes dependendo do driver.

-- Mensagem completa, como o Firebird devolve
Dynamic SQL Error
SQL error code = -104
Token unknown - line 1, column 8
SELEC

Traduzindo: Dynamic SQL Error é o GDSCODE 335544569 (erro genérico de SQL preparado em tempo de execução) e -104 é o SQLCODE específico (token inesperado). Se o log guarda só o primeiro número, você perde a informação que resolve o caso.

Código Mensagem Família Causa mais comum
335544721 unable to complete network request to host Conexão Servidor fora, porta 3050 bloqueada, DNS errado
10054 connection reset by peer Conexão Rede caiu, firewall derrubou a sessão ociosa, serviço reiniciado
335544375 unavailable database Conexão/config Caminho errado, arquivo sem permissão, banco em restore
335544569 dynamic SQL error Sintaxe Query montada por concatenação com aspas erradas
335544665 violation of PRIMARY or UNIQUE KEY constraint Dados Duplicidade, generator atrasado, retry sem idempotência
335544578 validation error for column, value "*** null ***" Dados Coluna NOT NULL recebendo NULL
335544382 string right truncation Dados Valor maior que o VARCHAR da coluna
335544345 lock conflict on no wait transaction Concorrência Transação NO WAIT disputando a mesma linha
335544336 deadlock Concorrência Duas transações atualizando as mesmas tabelas em ordem inversa
-902 internal Firebird consistency check Motor Bug conhecido da versão ou corrupção de página

Essa tabela cobre a maior parte do que aparece em log de sistema legado. Vale mapear apenas os códigos que realmente ocorrem no seu ambiente — mapear os 2.000 códigos possíveis é trabalho sem retorno.

Por que aparece "error writing data to the connection"?

Esse texto não é um erro de SQL: ele indica que a conexão TCP morreu no meio do comando. O driver recebeu um socket fechado e o Firebird não conseguiu escrever a resposta.

As causas reais, em ordem de frequência:

  1. Firewall ou NAT derrubando conexão ociosa. Pool de conexão que fica 30 minutos parado e volta a usar é o caso clássico.
  2. Serviço do Firebird reiniciado durante o deploy ou por crash.
  3. fbclient.dll incompatível com a versão do servidor, ou 32 bits em processo de 64 bits.
  4. Rede instável em link VPN/WAN entre aplicação e banco.
  5. Timeout de rede menor que a duração da query (relatório longo que ninguém vê falhar até o fim).

Diagnóstico rápido: rode isql do mesmo servidor de aplicação para o host do banco. Se funciona no seu micro e falha na máquina da aplicação, o problema é infraestrutura, não código.

Como classificar o erro antes de tratá-lo?

Classifique por SQLSTATE, não por texto. A classe 08 do padrão SQL significa "connection exception" e é a única família em que repetir a operação faz sentido.

// PDO: separe erro de conexão (retry) de erro de dados (não repita)
try {
    $stmt = $pdo->prepare('INSERT INTO pedidos (cliente_id, valor) VALUES (?, ?)');
    $stmt->execute([$clienteId, $valor]);
} catch (PDOException $e) {
    $state = $e->errorInfo[1] ?? null;   // classe SQLSTATE
    $msg   = $e->errorInfo[2] ?? $e->getMessage();
    error_log(sprintf('sqlstate=%s gdscode=%s msg=%s sql=%s',
        $e->getCode(), $state, $msg, $stmt->queryString));

    if ($e->getCode() === '08006' || $e->getCode() === '08003') {
        // conexão caiu: reconectar e repetir uma vez
        return $this->executarComReconexao($sqlOriginal, $params);
    }
    throw $e; // unique key, truncation, validation: repetir não resolve
}

Repetir um 335544665 (unique key) só gera mais erro e mais log. Repetir um 08006 resolve a maior parte das falhas de rede — desde que a operação seja idempotente.

Como funciona no driver legado ibase_*?

Nas extensões interbase/ibase (ainda comuns em PHP 5 e 7 antigos), o código vem em funções separadas. Guardar apenas ibase_errmsg() perde o número e impede classificação automática.

$conn = ibase_connect($dsn, $user, $pass, 'UTF8', 0, 3);
$res  = @ibase_query($conn, $sql);
if ($res === false) {
    error_log(json_encode([
        'errcode' => ibase_errcode(),  // GDSCODE numérico
        'errmsg'  => ibase_errmsg(),   // mensagem completa
        'sql'     => $sql,
        'user'    => $_SESSION['usuario'] ?? 'cli',
    ], JSON_UNESCAPED_UNICODE));
}

Com o GDSCODE em mão, a classificação vira switch ($code) e a mensagem técnica nunca precisa chegar à tela do usuário.

Como descobrir qual constraint violou a unique key?

A mensagem já entrega o nome do constraint e da tabela, mas o nome costuma ser gerado (INTEG_23) e não diz qual coluna duplicou. A consulta abaixo traduz o identificador em colunas reais.

-- Troque INTEG_23 pelo nome que apareceu na mensagem de erro
SELECT
    rc.RDB$RELATION_NAME AS tabela,
    isg.RDB$FIELD_NAME   AS coluna,
    rc.RDB$CONSTRAINT_TYPE AS tipo
FROM RDB$RELATION_CONSTRAINTS rc
JOIN RDB$INDEX_SEGMENTS isg
  ON isg.RDB$INDEX_NAME = rc.RDB$INDEX_NAME
WHERE rc.RDB$CONSTRAINT_NAME = 'INTEG_23'
ORDER BY isg.RDB$FIELD_POSITION;

Com a tabela e a coluna em mãos, o próximo passo é sempre o mesmo: verificar se o generator está sincronizado com o MAX(id). Esse desalinhamento é a causa mais comum de unique key em sistema que roda há anos — e tem solução própria.

Escrever consulta de diagnóstico contra RDB$ no meio do incidente é onde se perde tempo. O Sqlify transforma a descrição em português na query pronta, com cada cláusula explicada.

Como reproduzir o erro fora de produção?

Reproduzir com isql separa problema de banco de problema de aplicação. Dialect 3 é o padrão do Firebird moderno; um cliente conectando em dialect 1 muda o comportamento de datas e de NUMERIC.

# Conecta no banco remoto e deixa a sessão aberta
isql -user SYSDBA -password sua_senha 10.0.0.15/3050:/dados/empresa.fdb

SQL> SET SQL DIALECT 3;
SQL> SELECT CURRENT_TRANSACTION FROM RDB$DATABASE;  -- transação aberta
SQL> SELECT COUNT(*) FROM clientes;

Se o mesmo comando funciona no isql e falha na aplicação, o problema está no driver, no pool ou na query que a aplicação monta — quase sempre concatenação de string.

Como ver quem está segurando a conexão?

O monitor do Firebird (MON$) fica disponível a partir da versão 2.1 e mostra conexões e transações vivas sem instalar nada. É a primeira pergunta a fazer quando o erro é de concorrência.

SELECT a.MON$ATTACHMENT_ID, a.MON$USER, a.MON$REMOTE_ADDRESS,
       t.MON$TRANSACTION_ID, t.MON$TIMESTAMP, t.MON$LOCK_TIMEOUT
FROM MON$ATTACHMENTS a
JOIN MON$TRANSACTIONS t ON t.MON$ATTACHMENT_ID = a.MON$ATTACHMENT_ID
WHERE t.MON$STATE = 1               -- 1 = transação ativa
ORDER BY t.MON$TIMESTAMP;

Uma transação ativa há horas, de um usuário que já saiu do sistema, explica tanto lock conflict quanto crescimento de versões de registro. O detalhamento desse diagnóstico está em monitorar queries lentas no Firebird.

Checklist: o que ter antes do próximo incidente

  • Log registrando SQLSTATE, GDSCODE, query e usuário — sem valores de bind
  • Mensagem técnica nunca exibida ao usuário final
  • Retry automático apenas para a classe 08 (conexão)
  • fbclient de 32/64 bits igual ao processo PHP
  • Acesso ao MON$ pronto para uso em incidente
  • gfix -validate agendado em rotina de manutenção
  • Versão do Firebird anotada e com patch disponível

Para corrupção suspeita (SQLCODE -902), o caminho é gfix -validate -full com o banco offline:

# Banco deve estar parado; validate não corrige, só aponta
gfix -validate -full -user SYSDBA -password sua_senha /dados/empresa.fdb

Se o -902 aparece sempre no mesmo trecho de código, procure no changelog da sua versão: costuma ser bug do motor já corrigido em release posterior.

Perguntas frequentes

O que é o erro 902 do Firebird?

SQLCODE -902 é internal Firebird consistency check: o motor encontrou um estado que não deveria existir. Não é erro de SQL e não se resolve reescrevendo a query. Trate como sinal de bug da versão ou de corrupção de página: atualize o Firebird, rode gfix -validate -full e compare o resultado antes de qualquer restore.

O erro 10054 é do Firebird?

Não. O 10054 é WSAECONNRESET do Winsock: a conexão TCP foi encerrada pelo outro lado. O Firebird só repassa o que a camada de rede informou. A investigação é de infraestrutura — firewall, NAT, reinício do serviço, VPN.

O que significa o erro 104?

SQL error code = -104 é erro de sintaxe na query dinâmica: token inesperado, palavra reservada usada como nome de coluna, aspas simples não escapadas. Quando aparece em produção e não em teste, quase sempre é uma query montada por concatenação com valor contendo apóstrofo. O uso de bind parameters resolve a causa e ainda fecha a porta para SQL injection.

Por que o mesmo erro tem números diferentes entre versões?

O Firebird reorganizou parte da tabela de mensagens na versão 3.0, então o GDSCODE de uma falha pode mudar entre 2.5 e 3.0. Por isso a classificação confiável é pelo SQLSTATE (-104, -901, -904, classe 08), que segue o padrão SQL, e não pelo número longo.

Conclusão

Erro do Firebird se resolve em duas etapas: identificar a família (conexão, concorrência, dados ou sintaxe) e só então olhar o detalhe. Log com SQLSTATE e GDSCODE completo elimina metade da investigação, e MON$ responde o resto. O Sqlify traduz a descrição em português para a query pronta, com cada cláusula explicada — inclusive para quem precisa montar o diagnóstico em SQL.

Leia também: