JSON-RPC -32600 Invalid Request significa que el servidor analizó tu cuerpo como JSON pero el valor resultante no cumplía el contrato del objeto Request de JSON-RPC 2.0. La especificación define un orden de validación estricto: el cuerpo debe analizarse (de lo contrario -32700 Parse error), el valor analizado debe ser un Object o un Array no vacío (de lo contrario -32600), los miembros del envelope deben cumplir la Sección 4 (de lo contrario -32600), y solo después se examinan los params (de lo contrario -32602 Invalid params). La mayoría de los incidentes de -32600 en producción provienen de un cuerpo truncado o concatenado que aún se analiza, un productor que emite jsonrpc: "1.0" u omite el miembro por completo, o un batch hecho a mano que envía un array vacío o un elemento que no es objeto. Este artículo te ofrece un validador de envelope Node.js ejecutable, una lista de verificación previa para CI y una tabla de resultados reproducible para que puedas comprobar el comportamiento contra tu propio endpoint en lugar de confiar en una publicación de blog.
El código -32600 Invalid Request en la especificación JSON-RPC 2.0
La especificación JSON-RPC 2.0 define -32600 Invalid Request en la Sección 5.1.1 como uno de los cinco códigos de error predefinidos, junto con -32700 Parse error, -32601 Method not found, -32602 Invalid params y -32603 Internal error. El código está reservado para el caso en que el servidor recibió un valor que se analizó como JSON pero no es un objeto Request válido. No es un error de transporte, ni de autenticación, ni un fallo a nivel de método: es una declaración sobre la forma del envelope que enviaste.
La Sección 4 de la misma especificación define el objeto Request: un miembro jsonrpc que DEBE ser exactamente la cadena "2.0", un miembro method que DEBE ser una cadena, un miembro opcional params que DEBE ser un valor estructurado (un Array o un Object), y un miembro opcional id que DEBE ser una cadena, un número o Null. Un servidor que valida estrictamente rechazará cualquier desviación de esas restricciones con -32600 antes de siquiera comprobar si el método existe o si los argumentos son correctos.
La especificación Ethereum JSON-RPC añade una superficie de métodos concreta (eth_call, eth_getBalance, eth_sendRawTransaction, etc.) sobre ese envelope, pero no cambia las reglas del envelope. El mismo contrato de jsonrpc, method, params e id se aplica a todos los endpoints de nodos Ethereum, por lo que un validador escrito una vez contra la especificación base funciona en todos los proveedores. Para una orientación más amplia sobre endpoints Ethereum, consulta la página de la red Ethereum.
- Fuente autorizada: especificación JSON-RPC 2.0, Sección 4 (objeto Request) y Sección 5.1.1 (códigos de error predefinidos), https://www.jsonrpc.org/specification
- Fuente autorizada: especificación Ethereum JSON-RPC, https://ethereum.github.io/execution-apis/api-docs/
- Comportamiento documentado: se devuelve -32600 cuando el valor analizado no es un objeto Request válido; es distinto de -32700 (el cuerpo no se analizó) y de -32602 (envelope válido, params inválidos).
Orden de validación: por qué el código que recibes es un diagnóstico, no una lotería
Un servidor conforme no evalúa todas las reglas a la vez. Sigue un orden fijo, y la primera regla que falla determina el código que ves. Ese orden es lo que convierte el código de error en un diagnóstico: -32700 te dice que los bytes nunca se convirtieron en JSON, -32600 te dice que el JSON no era un envelope válido, y -32602 te dice que el envelope estaba bien pero los argumentos no.
El orden es: primero, el cuerpo debe analizarse como JSON; si no lo hace, el servidor devuelve -32700 Parse error. Segundo, el valor analizado debe ser un Object o un Array no vacío; si es una cadena, número, booleano o un array vacío, el servidor devuelve -32600 Invalid Request. Tercero, los miembros de ese objeto (o de cada elemento del array) deben cumplir la Sección 4; si jsonrpc falta o no es "2.0", si method falta o no es una cadena, si params está presente pero no es un Array u Object, o si id está presente pero no es una cadena, número o Null, el servidor devuelve -32600. Solo en cuarto lugar, una vez que el envelope es válido, se examinan los params contra la firma del método, momento en el que una discrepancia produce -32602.
Este orden importa porque te dice dónde buscar. Si recibes -32600, el problema está en tu serialización o en tu biblioteca cliente, no en tu codificación ABI. Si recibes -32602, el envelope está bien y el problema está en los argumentos. El artículo complementario sobre validación de parámetros inválidos JSON-RPC -32602 cubre esa segunda etapa en detalle.
- El cuerpo no se analiza como JSON → -32700 Parse error
- El valor analizado no es un Object o un Array no vacío → -32600 Invalid Request
- Los miembros del envelope violan la Sección 4 → -32600 Invalid Request
- Envelope válido pero los params no coinciden con el método → -32602 Invalid params
Un validador de envelope Node.js ejecutable que predice el código del servidor
La forma más fiable de hacer que -32600 sea una imposibilidad en tiempo de código es validar el envelope en tu propio proceso antes de que la solicitud se serialice y se envíe. El validador de abajo devuelve el código exacto de la especificación que habría devuelto el servidor, de modo que una prueba fallida te dice qué regla incumpliste en lugar de dejarte adivinar a partir de un log de producción.
La función comprueba jsonrpc === "2.0" sin espacios en blanco al principio ni al final y sin desviación de versión como "2" o "1.0", requiere que method sea una cadena no vacía, acepta params solo como un Array o un Object plano (o ausente), y acepta id solo como cadena, número o null. Los números fraccionarios se marcan como advertencia en lugar de fallo grave, porque la especificación los desaconseja pero no los prohíbe. Ejecútalo en una prueba unitaria contra cada solicitud que tu aplicación pueda construir.
// envelope-validator.js — predicts the JSON-RPC 2.0 error code for a request envelope
function validateEnvelope(value) {
// Rule 1: parsed value must be an Object or a non-empty Array
if (Array.isArray(value)) {
if (value.length === 0) {
return { code: -32600, message: 'Invalid Request', reason: 'empty batch array' };
}
return value.map((el, i) => ({ index: i, ...validateEnvelope(el) }));
}
if (value === null || typeof value !== 'object') {
return { code: -32600, message: 'Invalid Request', reason: 'not an object' };
}
// Rule 2: jsonrpc must be exactly the string "2.0"
if (value.jsonrpc !== '2.0') {
return { code: -32600, message: 'Invalid Request', reason: 'jsonrpc must be exactly "2.0"' };
}
// Rule 3: method must be a non-empty string
if (typeof value.method !== 'string' || value.method.length === 0) {
return { code: -32600, message: 'Invalid Request', reason: 'method must be a non-empty string' };
}
// Rule 4: params, if present, must be an Array or a plain Object
if ('params' in value) {
const p = value.params;
const isPlainObject = p !== null && typeof p === 'object' && !Array.isArray(p);
if (!Array.isArray(p) && !isPlainObject) {
return { code: -32600, message: 'Invalid Request', reason: 'params must be an Array or Object' };
}
}
// Rule 5: id, if present, must be a String, Number, or Null
if ('id' in value) {
const id = value.id;
const ok = id === null || typeof id === 'string' || typeof id === 'number';
if (!ok) {
return { code: -32600, message: 'Invalid Request', reason: 'id must be String, Number, or Null' };
}
if (typeof id === 'number' && !Number.isInteger(id)) {
return { code: -32600, message: 'Invalid Request', reason: 'fractional id is discouraged', warning: true };
}
}
return { code: 0, message: 'valid envelope' };
}
module.exports = { validateEnvelope };
// Example usage in a test:
// const { validateEnvelope } = require('./envelope-validator');
// const result = validateEnvelope({ jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 });
// console.assert(result.code === 0, result);Las dos causas de producción más comunes de -32600
La primera causa es un cuerpo que aún se analiza como JSON pero no es un único objeto. Dos objetos JSON escritos uno tras otro —por ejemplo {"jsonrpc":"2.0",...}{...}— a menudo serán aceptados por un analizador permisivo que lee el primer valor e ignora los bytes finales, o rechazados directamente según el analizador. Un cuerpo envuelto en un flujo delimitado por saltos de línea, donde el cliente concatena varias solicitudes en un solo cuerpo HTTP, produce la misma clase de fallo. El servidor ve un valor que no es un único objeto Request y devuelve -32600.
La segunda causa es un productor que emite una versión como "1.0" u omite el miembro jsonrpc por completo, mientras que la biblioteca cliente oculta el envelope al desarrollador. Esto ocurre con mayor frecuencia cuando un cliente HTTP hecho a mano, un proxy o una capa de middleware construyen el cuerpo por sí mismos y el código de la aplicación solo proporciona method y params. La solución es afirmar el envelope en el límite donde se construye, no donde se consume.
Ambas causas comparten una firma: la solicitud parece correcta en el código de la aplicación, el error aparece solo contra servidores estrictos, y el mismo payload funciona contra uno permisivo. Esa asimetría es exactamente por lo que la validación del envelope pertenece a CI en lugar de a una revisión posterior al incidente. La guía de decodificación del objeto de error JSON-RPC explica cómo leer los campos code, message y data una vez que llega el error.
- Cuerpos concatenados o delimitados por saltos de línea que se analizan pero no son un único objeto
- Productores que emiten jsonrpc: "1.0" u omiten el miembro por completo
- Middleware o proxies que reescriben el cuerpo sin volver a validar el envelope
- Bibliotecas cliente que ocultan el envelope y exponen solo method y params
Cómo las solicitudes batch cambian la superficie de fallo
Una solicitud batch es un Array de objetos Request, y la especificación trata un array vacío como un Invalid Request por derecho propio: el servidor devuelve un único objeto de error con código -32600 e id null. Ese es un comportamiento documentado, no una peculiaridad del proveedor, y sorprende a equipos que construyen batches dinámicamente y ocasionalmente producen una lista vacía.
Para un batch no vacío, la especificación requiere que el servidor procese cada elemento de forma independiente. Un array de un solo elemento que no es un objeto Request válido produce una entrada Invalid Request para ese elemento en lugar de fallar todo el batch. Esta es la clase de error que hace que el batching hecho a mano sea poco fiable: un elemento malformado corrompe silenciosamente el array de respuestas, y correlacionar el error con la solicitud infractora requiere que el campo id esté presente y bien tipado. El artículo complementario sobre correlación de id JSON-RPC y orden de batch cubre cómo mapear respuestas a solicitudes cuando algunos elementos fallan.
La regla práctica es validar cada elemento de un batch con el mismo validador que usas para una sola solicitud, y rechazar un batch vacío antes de que se serialice. Si tu constructor de batches puede producir un array vacío, puede producir un -32600, y el error llegará con id null, lo que dificulta atribuirlo a un llamador específico.
- Array vacío [] → respuesta única -32600 con id null
- Array no vacío → cada elemento se valida de forma independiente
- Un elemento inválido → una entrada Invalid Request, no un fallo de todo el batch
- Valida cada elemento con el mismo validador de envelope usado para solicitudes individuales
Por qué el tipo de id es parte del contrato del envelope
El miembro id es opcional, pero cuando está presente DEBE ser una cadena, un número o Null. Un id null es válido solo para respuestas y para las solicitudes cuyo id no se pudo detectar —por ejemplo, una solicitud que falló la validación del envelope antes de que el servidor pudiera leer su id. Un cliente que acuña ids de objeto, como { id: { requestId: 1 } }, crea un Invalid Request en un servidor estricto aunque el resto del envelope sea correcto.
Los números fraccionarios están explícitamente desaconsejados por la especificación. Un servidor puede aceptar id: 1.5 o puede rechazarlo; la especificación no garantiza ninguno de los dos comportamientos, por lo que depender de ello es un riesgo de portabilidad. Usa enteros o cadenas para los ids, y mantenlos únicos dentro de un batch para que las respuestas se puedan correlacionar de forma fiable.
El tipo de id también afecta a cómo lees el error. Cuando un servidor devuelve -32600 para una solicitud cuyo id no pudo detectar, la respuesta de error lleva id null. Cuando devuelve -32600 para una solicitud cuyo id era legible pero cuyo envelope era inválido por lo demás, la respuesta de error puede repetir ese id. Esa distinción es útil cuando estás clasificando un fallo de batch y necesitas saber si el servidor pudo atribuir el error en absoluto.
- Tipos de id válidos: String, Number, Null
- Los ids de objeto son inválidos y producen -32600 en servidores estrictos
- Los números fraccionarios están desaconsejados y pueden ser rechazados
- id null en una respuesta de error a menudo significa que el servidor no pudo detectar el id de la solicitud
Normalización y lista de verificación previa para CI
Una lista de verificación previa convierte la validación del envelope de un ejercicio de depuración en una puerta en tiempo de compilación. Añade las siguientes comprobaciones a tu suite de pruebas para que un envelope malformado nunca llegue a la red. Cada comprobación se asigna a una regla de la especificación, por lo que un fallo te dice exactamente qué regla incumpliste.
La lista es deliberadamente pequeña. Cubre los miembros que la especificación valida, los casos de batch que son fáciles de equivocar y el límite de serialización donde aparecen los errores de concatenación. Si tu aplicación construye solicitudes en más de un lugar, ejecuta la lista contra cada sitio de construcción, no solo contra el principal.
- Afirma que jsonrpc === "2.0" sin espacios en blanco al principio ni al final
- Afirma que method es una cadena no vacía
- Afirma que params está ausente, es un Array o un Object plano
- Afirma que id está ausente, es una cadena, un número entero o Null
- Afirma que un batch es un Array no vacío y que cada elemento pasa las mismas comprobaciones
- Afirma que el cuerpo serializado contiene exactamente un valor JSON, sin bytes finales
- Afirma que la cabecera Content-Type es application/json para que el servidor analice el cuerpo como JSON
- Ejecuta el validador en una prueba unitaria contra cada solicitud que tu aplicación pueda construir
Medición reproducible: una tabla de resultados para completar contra tu propio endpoint
El comportamiento documentado te dice lo que exige la especificación; no te dice lo que devuelve tu proveedor específico. Para obtener evidencia reproducible, reproduce un envelope deliberadamente roto por fila contra tu propio endpoint y registra el código del servidor, el mensaje del servidor, el estado HTTP y el tiempo transcurrido. La tabla de abajo es una plantilla: complétala con tus propias mediciones en lugar de confiar en números de cualquier artículo, incluido este.
Envía cada variante como un POST HTTP sin procesar con Content-Type: application/json, y captura el cuerpo completo de la respuesta. Algunos proveedores responden HTTP 400 con un cuerpo que no es un objeto de error JSON-RPC en absoluto, por lo que la columna de estado HTTP importa tanto como la columna de código. Si estás comparando proveedores, ejecuta la misma tabla contra cada endpoint y guarda las respuestas sin procesar junto a la tabla. La guía de endpoints RPC explica cómo obtener y configurar endpoints para este tipo de comparación.
- Variante rota: jsonrpc: "1.0" — registra código del servidor, mensaje, estado HTTP, ms transcurridos
- Variante rota: miembro jsonrpc omitido — registra código del servidor, mensaje, estado HTTP, ms transcurridos
- Variante rota: method es un número — registra código del servidor, mensaje, estado HTTP, ms transcurridos
- Variante rota: params es una cadena — registra código del servidor, mensaje, estado HTTP, ms transcurridos
- Variante rota: id es un objeto — registra código del servidor, mensaje, estado HTTP, ms transcurridos
- Variante rota: dos objetos JSON concatenados — registra código del servidor, mensaje, estado HTTP, ms transcurridos
- Variante rota: array de batch vacío [] — registra código del servidor, mensaje, estado HTTP, ms transcurridos
- Variante rota: batch con un elemento inválido — registra código del servidor, mensaje, estado HTTP, ms transcurridos
| Input variant | Observed code | Observed message | HTTP status | Elapsed ms |
| --- | --- | --- | --- | --- |
| jsonrpc: "1.0" | ____ | ____ | ____ | ____ |
| jsonrpc omitted | ____ | ____ | ____ | ____ |
| method is a number | ____ | ____ | ____ | ____ |
| params is a string | ____ | ____ | ____ | ____ |Limitaciones, variación entre proveedores y validación a nivel de transporte
La especificación reserva el rango -32000 a -32768 para errores definidos por la implementación, por lo que un proveedor PUEDE devolver un código no estándar para la misma condición. Un servidor que devuelve -32000 para un envelope malformado no está violando la especificación; está usando el rango definido por la implementación. Esto significa que tu manejo de errores no debe asumir que -32600 es el único código que puede producir un envelope malformado. Trata el código como una señal fuerte, no como una garantía.
Algunos proveedores responden HTTP 400 con un cuerpo que no es un objeto de error JSON-RPC en absoluto —por ejemplo, una página de error HTML o un mensaje de texto plano de un balanceador de carga. Eso es un fallo a nivel de transporte, no de envelope, y debe manejarse por separado. Tu cliente debe comprobar el estado HTTP y el Content-Type antes de intentar analizar el cuerpo como una respuesta JSON-RPC, y debe mostrar una clase de error distinta cuando el cuerpo no es un objeto de error JSON-RPC.
El comportamiento específico del proveedor varía. La especificación define el contrato del envelope, pero no exige un estado HTTP concreto, una cadena de mensaje concreta ni un tratamiento concreto de casos límite como los ids fraccionarios. Donde este artículo describe el comportamiento del proveedor, trátalo como documentado o varía según el proveedor, y verifícalo contra tu propio endpoint usando la tabla de resultados anterior. Para una discusión más amplia sobre cómo elegir entre proveedores, consulta las páginas de precios de RPC y servicio de API.
- El rango -32000 a -32768 está reservado para errores definidos por la implementación; un proveedor puede usarlo para la misma condición
- Algunos proveedores devuelven HTTP 400 con un cuerpo que no es JSON-RPC; maneja los errores de transporte por separado
- Las cadenas de mensaje y los estados HTTP varían según el proveedor y no están especificados
- Verifica el comportamiento del proveedor contra tu propio endpoint en lugar de asumir un único código
Flujo de trabajo de solución de problemas para un -32600 en producción
Cuando aparece un -32600 en producción, el camino más rápido hacia una solución es capturar el cuerpo de la solicitud sin procesar y reproducirlo contra el validador. Si el validador devuelve -32600, el error está en tu serialización o en tu biblioteca cliente. Si el validador devuelve 0, el error está en la capa de transporte —un proxy, un middleware o un balanceador de carga que reescribió el cuerpo— y deberías inspeccionar los bytes en el cable en lugar del objeto en el código de tu aplicación.
Si el validador devuelve -32700, el cuerpo nunca se analizó como JSON, lo que normalmente significa truncamiento o un desajuste de Content-Type. Si devuelve -32602, el envelope está bien y el problema está en los argumentos, lo que es una investigación diferente. El artículo complementario sobre decodificación de razones de revert y errores personalizados de Ethereum cubre los fallos a nivel de método que se encuentran detrás de un envelope válido.
Para fallos de batch, comprueba si la respuesta de error lleva id null. Si es así, el servidor no pudo atribuir el error a una solicitud específica, lo que normalmente significa que el batch en sí estaba malformado —un array vacío, o un cuerpo que no era un array en absoluto. Si la respuesta de error repite un id, el batch era legible y un elemento era inválido; valida cada elemento individualmente para encontrarlo.
- Captura el cuerpo de la solicitud sin procesar, no el objeto de la aplicación
- Reproduce el cuerpo contra el validador para localizar el fallo
- El validador devuelve -32600 → error de serialización o de biblioteca cliente
- El validador devuelve -32700 → truncamiento o desajuste de Content-Type
- El validador devuelve -32602 → el envelope está bien, los argumentos son incorrectos
- La respuesta de error lleva id null → el batch en sí estaba malformado
Próximos pasos: hacer que la validación del envelope forme parte de la compilación
El objetivo es mover -32600 de un incidente de producción a una prueba unitaria fallida. Añade el validador de este artículo a tu suite de pruebas, ejecútalo contra cada solicitud que tu aplicación pueda construir y haz fallar la compilación cuando devuelva un código distinto de cero. Añade la tabla de resultados a tu lista de verificación de evaluación de proveedores para tener evidencia reproducible de cómo se comporta cada endpoint antes de depender de él.
Si estás construyendo contra Ethereum, empieza por la página de la red Ethereum para confirmar la superficie de métodos, y usa el centro de aprendizaje de OnFinality para encontrar los artículos complementarios sobre objetos de error, validación de params y correlación de id. Para la configuración de endpoints y la selección de proveedores, la guía de endpoints RPC y la página de precios de RPC cubren el lado operativo. La página del servicio de API describe la oferta de endpoints gestionados si prefieres no ejecutar tus propios nodos.
La validación del envelope es una pequeña cantidad de código con un gran beneficio: elimina toda una clase de errores de producción, hace que los errores restantes sean más fáciles de diagnosticar y te da una forma reproducible de comparar proveedores. La especificación es corta y estable, por lo que el validador que escribas hoy seguirá siendo correcto cuando la superficie de métodos crezca.
- Añade el validador a tu suite de pruebas unitarias y haz fallar la compilación con un código distinto de cero
- Añade la tabla de resultados a tu lista de verificación de evaluación de proveedores
- Vuelve a ejecutar la tabla cada vez que cambies de proveedor o actualices una biblioteca cliente
- Conserva el cuerpo de la solicitud sin procesar en tus logs para que los errores de producción se puedan reproducir