Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Solución de problemas de RPC13 min de lectura

JSON-RPC -32700 Parse Error: diagnóstico de payloads malformados

Un manual determinista para encontrar el byte exacto que hace que un servidor JSON-RPC devuelva -32700 Parse error, con evidencia de captura y reproducción y una tabla de resultados para completar.

TL;DR

Un error -32700 Parse error significa que el servidor recibió bytes que no son JSON válido, por lo que nunca validó jsonrpc, method, params ni id, y la respuesta de error siempre lleva id null. Por lo tanto, el fallo está en el transporte o la codificación, no en la lógica de tu aplicación: cuerpos truncados, escrituras concatenadas en una conexión keep-alive, payloads doblemente codificados, encabezados Content-Encoding ausentes, bytes no UTF-8 y discrepancias de Content-Type son las causas habituales. El método fiable consiste en registrar los bytes exactos enviados (longitud más hash), el Content-Type y Content-Encoding realmente aplicados, y el cuerpo de respuesta sin procesar; luego reproducir esos bytes con curl para que el fallo sea reproducible fuera de la aplicación. Nunca reintentes un -32700 a ciegas; corrige el codificador o el proxy y reconstruye el payload primero. La especificación JSON-RPC 2.0 fija el código y el id null, pero no fija el estado HTTP, así que lee la capa HTTP y el objeto de error juntos.

Qué informa realmente el error -32700 Parse Error

La especificación JSON-RPC 2.0, Sección 5.1.1, define -32700 Parse error como el código que se devuelve cuando el servidor recibe JSON no válido. Esa única frase contiene todo el diagnóstico: el servidor intentó analizar el cuerpo de la solicitud como JSON, el análisis falló y la ejecución se detuvo antes de examinar cualquier miembro del objeto de solicitud. Nunca ocurrió una comprobación de versión jsonrpc, una búsqueda de método, una validación de params ni una extracción de id.

Como no se pudo detectar ningún id de solicitud, la respuesta de error lleva id null. Esto no es un error del proveedor; es el comportamiento especificado, y es la señal más fuerte de que estás ante un fallo de codificación y no ante un fallo de la aplicación. Compáralo con un fallo de validación como JSON-RPC -32602 invalid params validation, donde el servidor sí analizó el sobre, sí leyó tu id y lo devuelve en la respuesta.

La Sección 6 de la misma especificación añade una regla específica para lotes: si el cuerpo del lote en sí no es JSON válido, el servidor devuelve un único objeto de error en lugar de un array de respuestas. Un array vacío es JSON válido y es un caso completamente distinto, por lo que las consultas con forma de lote a menudo generan hilos no relacionados. Para las reglas de ordenación que se aplican una vez que el cuerpo sí se analiza, consulta JSON-RPC batching best practices.

  • Documentado: -32700 se devuelve cuando se recibe JSON no válido (JSON-RPC 2.0, Sección 5.1.1).
  • Documentado: la respuesta de error usa id null porque no se pudo detectar ningún id de solicitud.
  • Documentado: un lote cuyo cuerpo no es JSON válido produce un solo objeto de error, no un array (Sección 6).
  • Documentado: la especificación no exige un código de estado HTTP para esta condición.

Por qué un fallo de análisis es un fallo de transporte y codificación

Los errores de aplicación ocurren después de que se entiende el sobre: un método que no existe, un parámetro del tipo incorrecto, una llamada a contrato que revierte. Un error de análisis ocurre antes de que exista el sobre. Los bytes en el cable no son un documento JSON en absoluto, así que no hay nada que el nodo pueda enrutar, medir o autorizar. Por eso los tickets de -32700 que empiezan con "mi transacción falló" suelen estar mal etiquetados: nunca se construyó ninguna transacción.

La consecuencia práctica es que debes dejar de leer primero el código de tu aplicación y empezar leyendo primero el flujo de bytes. La pregunta no es "qué pretendía enviar mi código" sino "qué llevó realmente el socket". Todo lo que aparece en la lista de causas clasificadas a continuación es un defecto a nivel de byte, y todos ellos son reproducibles una vez que capturas los bytes.

Esta distinción también explica por qué el objeto de error es escueto. No hay un miembro data con una traza de pila, porque el servidor no tiene contexto que describir. Si quieres entender cómo se relacionan normalmente code, message y data, consulta Decoding the JSON-RPC error object.

Causas en producción ordenadas por frecuencia observada

El siguiente orden refleja la frecuencia con la que aparece cada causa en informes de incidentes reales. Trátalo como un orden de triaje, no como una afirmación estadística: empieza por arriba y baja, porque las dos primeras causas explican la mayoría de los casos en los que un payload parece correcto en un depurador pero falla en producción.

El truncamiento es primero porque es invisible en los registros de la aplicación. Un proxy, un balanceador de carga o un tiempo de espera del cliente pueden cerrar la escritura a mitad del cuerpo, y el servidor recibe un prefijo de JSON válido que termina abruptamente. La concatenación es segunda porque la lógica de reintento con frecuencia reenvía sin descartar la primera escritura, por lo que una conexión keep-alive lleva dos cuerpos seguidos y el analizador ve un documento no válido.

Las causas restantes son defectos de codificación: un payload doblemente codificado de modo que el servidor recibe una cadena JSON que contiene JSON en lugar de un objeto; bytes binarios o comprimidos enviados sin el encabezado Content-Encoding correspondiente, por lo que el servidor intenta analizar gzip como UTF-8; una secuencia de bytes no UTF-8 inyectada por un generador de JSON por concatenación de cadenas; y un Content-Type que no coincide con el cuerpo, como datos codificados de formulario enviados por POST a un endpoint que espera application/json.

  • Cuerpo truncado a mitad de escritura por un proxy, un balanceador de carga o un tiempo de espera del cliente.
  • Dos cuerpos concatenados en una conexión keep-alive tras un reintento que no descartó la primera escritura.
  • Payload doblemente codificado: el servidor recibe una cadena JSON que contiene JSON en lugar de un objeto.
  • Bytes comprimidos o binarios enviados sin el encabezado Content-Encoding correspondiente.
  • Secuencia de bytes no UTF-8 inyectada por concatenación manual de cadenas.
  • Content-Type que no coincide con el cuerpo, como datos codificados de formulario enviados a un endpoint JSON.

Procedimiento de captura y comparación para convertir un error vago en evidencia

Un informe de -32700 solo es accionable cuando puedes indicar los bytes exactos que se enviaron. Instrumenta el cliente en el último momento posible antes de la escritura en el socket, no en el punto donde construyes el objeto de solicitud, porque los proxies y las bibliotecas HTTP pueden transformar el cuerpo después de que tu código haya terminado con él.

Registra cuatro cosas juntas: la longitud en bytes del cuerpo serializado, un hash de esos bytes, los encabezados Content-Type y Content-Encoding realmente aplicados a la solicitud, y el cuerpo de respuesta sin procesar tal como se recibió. El hash importa porque te permite demostrar que los bytes que reproduces son los bytes que fallaron, y el cuerpo de respuesta sin procesar importa porque un WAF puede devolver HTML en lugar de un objeto de error JSON-RPC.

Luego reproduce los bytes registrados con curl, incluyendo los mismos encabezados, para que el fallo sea reproducible fuera de la aplicación. Si la reproducción tiene éxito, el defecto está en tu pila de cliente o en un intermediario; si la reproducción falla de forma idéntica, tienes el payload problemático en la mano y puedes bisecarlo byte a byte.

curl -sS -D - -o response.bin \
  -X POST 'https://your-endpoint.example/rpc' \
  -H 'Content-Type: application/json' \
  --data-binary @captured-body.bin

# Inspect what came back, byte for byte
wc -c captured-body.bin response.bin
head -c 400 response.bin; echo

# Confirm the captured body is valid JSON before blaming the server
node -e "const fs=require('fs');const b=fs.readFileSync('captured-body.bin');try{JSON.parse(b.toString('utf8'));console.log('body parses locally')}catch(e){console.log('local parse failure:',e.message)}"

Ejemplo ejecutable en Node.js: verifica antes de enviar

La solución permanente más barata es una aserción previa al vuelo en el cliente. Serializa con JSON.stringify, vuelve a analizar el resultado y confirma que obtienes un objeto en lugar de una cadena. Un payload doblemente codificado pasa una comprobación de veracidad ingenua pero falla esta prueba de ida y vuelta de inmediato.

El ejemplo siguiente también imprime el payload de error sin procesar, incluido el id null, para que puedas ver la diferencia entre un fallo de análisis y un error de aplicación en el mismo flujo de registro. Ejecútalo contra cualquier endpoint que controles, incluido uno de la página de la red Ethereum o tu propio despliegue del servicio de API.

const endpoint = process.env.RPC_URL || 'https://your-endpoint.example/rpc';

function buildBody(method, params) {
  const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
  const roundTrip = JSON.parse(body);
  if (typeof roundTrip !== 'object' || roundTrip === null || Array.isArray(roundTrip)) {
    throw new Error('Body did not round-trip to a JSON object; check for double encoding');
  }
  return body;
}

async function call(method, params) {
  const body = buildBody(method, params);
  const bytes = Buffer.byteLength(body, 'utf8');
  const hash = require('crypto').createHash('sha256').update(body).digest('hex').slice(0, 16);
  console.log('sending bytes=%d sha256=%s content-type=application/json', bytes, hash);

  const res = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body
  });

  const raw = await res.text();
  console.log('http status=%d raw=%s', res.status, raw.slice(0, 400));

  let parsed;
  try {
    parsed = JSON.parse(raw);
  } catch (e) {
    console.error('Response is not JSON; the HTTP layer is the real signal here');
    return;
  }

  if (parsed.error) {
    console.error('rpc error code=%s message=%s id=%s', parsed.error.code, parsed.error.message, parsed.id);
    if (parsed.error.code === -32700) {
      console.error('Parse failure: the server never read a request object. Fix the encoder or proxy, then rebuild the payload.');
    }
  } else {
    console.log('result=%o', parsed.result);
  }
}

call('eth_blockNumber', []).catch((e) => console.error('transport failure:', e.message));

Separar un error de análisis del cliente de uno del servidor

Los tickets de -32700 más engañosos son aquellos en los que el propio analizador del lector tiene la culpa. Si el cuerpo de la respuesta no es JSON válido, tu cliente no puede decodificarlo, y la excepción resultante a menudo se informa como "el nodo devolvió un error de análisis" cuando en realidad el nodo devolvió HTML, un cuerpo vacío o un flujo truncado. Lee el intercambio en ambas direcciones antes de asignar culpas.

La prueba es simple y mecánica: analiza tú mismo el cuerpo de respuesta sin procesar. Si se analiza y contiene un objeto de error con código -32700, el servidor rechazó los bytes de tu solicitud. Si no se analiza, el fallo está en la ruta de respuesta, y la señal correcta es el estado y los encabezados HTTP, no el objeto de error JSON-RPC.

Aquí también varía el comportamiento del proveedor. La semántica documentada de JSON-RPC está fijada por la especificación, pero el estado HTTP usado para un fallo de análisis no lo está, y algunos proveedores colocan delante de sus endpoints un firewall de aplicaciones web que responde con una página de desafío HTML. En ese caso, el objeto de error debe ignorarse por completo.

  • La respuesta se analiza y contiene el código -32700: el servidor rechazó los bytes de tu solicitud.
  • La respuesta no se analiza: el fallo está en la ruta de respuesta, así que lee el estado y los encabezados HTTP.
  • La respuesta es HTML: respondió un intermediario como un WAF, y el objeto de error JSON-RPC está ausente.
  • La respuesta está vacía: sospecha truncamiento o un reinicio de conexión en lugar de un defecto JSON.

Semántica de reintentos: por qué los reintentos a ciegas no pueden tener éxito

Un -32700 es determinista con respecto a los bytes que lo causaron. Los mismos bytes fallarán al analizarse en el siguiente intento, y en el siguiente, por lo que un bucle de reintento que reenvía un búfer sin cambios convierte un único fallo en una tasa de error sostenida y puede amplificar la carga en el endpoint. Esto es lo contrario de un fallo de red transitorio, donde reintentar es la respuesta correcta.

La acción correcta es corregir el codificador o el intermediario y luego reconstruir el payload desde cero. Solo después de que el cuerpo se haya regenerado, reserializado y verificado de nuevo debería emitirse un reintento. Si tu lógica de reintento vive en un cliente HTTP compartido, añade una protección que se niegue a reintentar cuando la respuesta contenga el código -32700.

A modo de comparación, un fallo interno transitorio como JSON-RPC -32603 internal error debugging puede reintentarse legítimamente con retroceso exponencial, porque la solicitud se entendió y el fallo ocurrió en sentido descendente. La distinción es si el servidor llegó a leer tu objeto de solicitud.

Tabla de resultados: evidencia reproducible de tu propio endpoint

Construye una tabla con un payload deliberadamente corrupto por fila y ejecútala contra tu propio endpoint. Esto convierte un error anecdótico en una matriz reproducible, y también revela cómo tu proveedor específico asigna los fallos de análisis a los códigos de estado HTTP, algo que la especificación deja abierto.

Rellena las columnas siguientes con los valores que observes. No copies números de ningún artículo, incluido este: el objetivo del ejercicio es que la evidencia provenga de tu endpoint, tu cadena de proxies y tu pila de cliente.

  • Corrupción aplicada: cuerpo truncado, cuerpos concatenados, cadena doblemente codificada, gzip sin Content-Encoding, byte UTF-8 no válido, cuerpo codificado de formulario.
  • Código del servidor: el código de error JSON-RPC observado, que se espera que sea -32700 para los casos de análisis.
  • Mensaje: la cadena de mensaje exacta devuelta, citada textualmente.
  • Estado HTTP: la línea de estado observada, que puede ser 400, 200 o 500 según el proveedor.
  • id devuelto: el miembro id en la respuesta, que se espera que sea null para fallos de análisis.
  • ms transcurridos: tiempo de reloj de pared desde la escritura hasta la lectura completa de la respuesta, medido por tu cliente.
| Corruption applied | Server code | Message | HTTP status | id echoed | Elapsed ms |
| --- | --- | --- | --- | --- | --- |
| ____ | ____ | ____ | ____ | ____ | ____ |
| ____ | ____ | ____ | ____ | ____ | ____ |
| ____ | ____ | ____ | ____ | ____ | ____ |
| ____ | ____ | ____ | ____ | ____ | ____ |

Limitaciones y compensaciones en el diagnóstico de errores de análisis

La especificación deliberadamente no fija el código de estado HTTP para un fallo de análisis. Algunos endpoints responden 400, otros responden 200 con un objeto de error en el cuerpo, y otros responden 500. Cualquier cliente que base su manejo de errores únicamente en el estado HTTP clasificará mal al menos uno de estos casos, así que maneja ambas capas explícitamente.

El comportamiento del proveedor también varía en el borde. Un firewall de aplicaciones web o una pasarela pueden interceptar un cuerpo malformado antes de que llegue al manejador JSON-RPC y devolver HTML, una redirección o una página de desafío. En esa situación, el objeto de error JSON-RPC no existe y no debe sintetizarse; la capa HTTP es la única señal fiable.

Por último, la captura a nivel de byte tiene sus propias compensaciones. Registrar cuerpos de solicitud completos puede exponer parámetros sensibles y aumenta el volumen de registros, así que prefiere registrar longitud más hash en producción y conserva los cuerpos completos solo en una ventana de depuración controlada. El hash es suficiente para demostrar que una reproducción coincide con el fallo original.

Lista de verificación para un incidente -32700 en vivo

Trabaja la lista en orden y detente en cuanto la reproducción reproduzca o elimine el fallo. Cada elemento está diseñado para eliminar una capa en lugar de adivinar una causa.

Si la reproducción tiene éxito con bytes y encabezados idénticos, el defecto está en tu pila de cliente o en un intermediario entre el cliente y el endpoint. Si falla de forma idéntica, biseca el cuerpo capturado: córtalo por la mitad, prueba cada mitad y continúa hasta aislar el rango de bytes problemático. Un solo byte extraviado basta para invalidar todo el documento.

  • Confirma que el cuerpo capturado se analiza localmente con un analizador JSON estricto antes de contactar con el servidor.
  • Confirma que Content-Type coincide con el cuerpo y que Content-Encoding coincide con la codificación real.
  • Confirma que ningún proxy está almacenando en búfer, reescribiendo o truncando el cuerpo; revisa su límite de tamaño de cuerpo.
  • Confirma que la lógica de reintento descarta la escritura anterior antes de reenviar en una conexión keep-alive.
  • Confirma que el cuerpo de la respuesta es JSON antes de interpretar cualquier código de error dentro de él.
  • Confirma que la URL del endpoint y el método son correctos; un GET a un endpoint solo POST puede aparecer como error de análisis en algunas pasarelas.

Próximos pasos y lecturas relacionadas

Una vez corregido el fallo de análisis, refuerza el cliente para que la misma clase de defecto no pueda repetirse: mantén la aserción de ida y vuelta, mantén la línea de registro de longitud y hash, y añade una protección que se niegue a reintentar en -32700. Luego revisa cómo se llega a tu endpoint, porque la mayoría de los fallos de análisis se originan en la ruta y no en el generador de payload.

Para la selección de endpoints y los patrones de conectividad, empieza por la guía de endpoints RPC y el centro OnFinality Learn más amplio. Si estás dimensionando o planificando capacidad para el tráfico que genera tu cliente, la página de precios de RPC describe las opciones comerciales, y la página de la red Ethereum enumera los endpoints contra los que puedes probar.

Para la semántica autoritativa del protocolo, lee la especificación JSON-RPC 2.0 en https://www.jsonrpc.org/specification, en particular las Secciones 5.1.1 y 6, y la especificación JSON-RPC de Ethereum en https://ethereum.github.io/execution-apis/ para ver cómo aparecen estos códigos en la práctica en endpoints de la capa de ejecución. Ambas son fuentes primarias; el comportamiento específico del proveedor, como la asignación de estados HTTP y la interceptación por WAF, se documenta por proveedor y varía.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar