Cuando un extrínseco de Polkadot/Substrate falla en la capa RPC, el error JSON-RPC '1010: Invalid Transaction' es un envoltorio cuyo campo data contiene una etiqueta de validez del pool de transacciones (por ejemplo, Payment, Stale, TemporarilyBanned) que revela la causa real. Para los extrínsecos que pasan el pool pero fallan durante la ejecución, el fallo aparece a través de los eventos de author_submitAndWatchExtrinsic y un DispatchError del runtime, que se decodifica usando los metadatos de la cadena. Este artículo explica el mecanismo, proporciona un script ejecutable de @polkadot/api para capturar y decodificar estos errores, y ofrece una lista de verificación de etiquetas y correcciones.
Respuesta directa: qué significa realmente la transacción inválida 1010
Cuando envías un extrínseco a un nodo de Polkadot o Substrate mediante author_submitExtrinsic o author_submitAndWatchExtrinsic, el pool de transacciones del nodo realiza una verificación de validez antes de aceptar la transacción. Si esa verificación falla, el RPC devuelve un error JSON-RPC con código 1010 y mensaje Invalid Transaction. La razón real está codificada en el campo data como una etiqueta como Payment, Stale o TemporarilyBanned. Esta etiqueta no es una cadena aleatoria; corresponde a las variantes del enum InvalidTransaction definidas en el marco del pool de transacciones de Substrate. Para corregir tu envío, debes decodificar esa etiqueta y abordar el problema subyacente, ya sea fondos insuficientes, un nonce incorrecto o un remitente baneado. Para los extrínsecos que pasan el pool pero fallan durante la ejecución, el fallo aparece más tarde como un DispatchError en los eventos de la transacción, que se decodifica usando los metadatos del runtime de la cadena.
Esta guía es parte del hub de aprendizaje de OnFinality y se centra en el ecosistema de Polkadot. Si eres nuevo en los endpoints RPC de Polkadot, consulta primero la guía de RPC de Polkadot. Para problemas a nivel de transporte como tiempos de espera o límites de velocidad, consulta nuestros artículos sobre tiempos de espera, latencia y límites de velocidad.
Cómo funciona el envío de extrínsecos bajo el capó
Enviar un extrínseco a un nodo de Polkadot es un proceso de dos etapas. Primero, el pool de transacciones valida el extrínseco contra el estado actual y las reglas del pool. Aquí es donde se origina el error 1010 Invalid Transaction. El pool verifica cosas como el nonce (si es el siguiente esperado), la longevidad de la transacción (si no es demasiado antigua) y la capacidad del remitente para pagar tarifas. Si alguna verificación falla, el pool devuelve una variante InvalidTransaction, que la capa RPC serializa en el campo data del error.
En segundo lugar, si el extrínseco pasa el pool, se propaga y se incluye en un bloque. La ejecución ocurre durante la producción de bloques. Si la llamada del extrínseco falla en el runtime, por ejemplo, debido a un origen incorrecto o un error específico del pallet, la transacción no se revierte; en su lugar, se incluye en el bloque pero se marca como fallida. El fallo se reporta a través del evento system.ExtrinsicFailed, que contiene un DispatchError. Para ver esto, debes usar author_submitAndWatchExtrinsic, que emite actualizaciones de transactionStatus incluyendo Invalid y Drop para rechazos del pool, y Finalized con el hash del bloque para inclusión exitosa. El DispatchError no es parte del error RPC; debes consultar los eventos del bloque para decodificarlo.
Este mecanismo está documentado en la documentación del pool de transacciones de Substrate y en la documentación de dispatch de FRAME. Los documentos para desarrolladores de Polkadot también cubren extrínsecos y transacciones.
Decodificación de la etiqueta de datos del error 1010
El campo data de un error 1010 Invalid Transaction es una cadena que coincide con una de las variantes InvalidTransaction del pool de transacciones de Substrate. Las más comunes que encontrarás son:
Payment– El remitente no puede pagar la tarifa de transacción (por ejemplo, saldo insuficiente o un cálculo de tarifa corrupto).
Stale– El nonce es demasiado bajo (ya usado) o la transacción es demasiado antigua.
Future– El nonce es más alto que el nonce actual de la cuenta (aún no válido).
TemporarilyBanned– El remitente está temporalmente baneado del pool, a menudo debido a demasiados envíos inválidos.
BadProof– La firma o el payload firmado es inválido.
AncientBirthBlock– Laera(mortalidad) de la transacción es demasiado antigua; el bloque de nacimiento está más allá delBlockHashCount.
ExhaustsResources– El pool está lleno o la transacción excedería los límites de peso del bloque.
Custom(u8)– Un error de validez específico de la cadena, a menudo de una extensión de transacción personalizada.
Para ver la etiqueta exacta, debes capturar el objeto de error en tu código de cliente. La etiqueta está en error.data (o error.data.toString() en algunas bibliotecas). No te bases solo en el mensaje, ya que es genérico.
La siguiente tabla mapea cada etiqueta a su causa típica y corrección. Esto se basa en el código fuente de Substrate y la experiencia de la comunidad.
- Payment – Causa: saldo insuficiente para tarifas o un problema relacionado con tarifas. Corrección: asegúrate de que la cuenta tenga suficiente saldo libre para cubrir la tarifa más cualquier depósito existencial; verifica la tarifa mediante
api.tx.balances.transfer.estimate. - Stale – Causa: nonce demasiado bajo o transacción demasiado antigua. Corrección: usa el nonce actual de
api.query.system.accounty establece unaeraadecuada (por ejemplo,api.tx.balances.transferconera: 64). - Future – Causa: nonce demasiado alto. Corrección: espera a que se procesen las transacciones anteriores o establece el nonce correcto.
- TemporarilyBanned – Causa: envíos inválidos repetidos del mismo remitente. Corrección: espera a que expire la prohibición (generalmente unos minutos) y corrige el problema subyacente.
- BadProof – Causa: firma o payload firmado inválido. Corrección: asegúrate de firmar con la cuenta correcta y de que el payload coincida con las extensiones firmadas de la cadena.
- AncientBirthBlock – Causa: la
erade la transacción es demasiado larga o el bloque de nacimiento es demasiado antiguo. Corrección: usa unaeramás corta o deja que la API la establezca automáticamente. - ExhaustsResources – Causa: el pool está lleno o la transacción excedería los límites del bloque. Corrección: reintenta más tarde o reduce la complejidad de la transacción.
- Custom(u8) – Causa: error de validez específico de la cadena. Corrección: consulta la documentación o el código fuente de la cadena para conocer el significado del código personalizado.
Ejemplo ejecutable: capturar y decodificar errores 1010
El siguiente script de Node.js usa @polkadot/api para conectarse a un endpoint WebSocket proporcionado por el usuario, construir y firmar un extrínseco de transferencia simple, y enviarlo. Captura el error JSON-RPC e imprime el objeto de error completo, incluido el campo data. También demuestra cómo usar author_submitAndWatchExtrinsic para capturar eventos de ejecución si la transacción es aceptada.
Requisitos previos: Node.js 18+, @polkadot/api versión 10.9.1 (a partir del 2026-09-05). Instala con npm install @polkadot/api. Reemplaza WS_URL con tu endpoint (por ejemplo, wss://rpc.polkadot.io para Polkadot, wss://statemint-rpc.polkadot.io para Asset Hub).
Importante: El script construye una transferencia de 0 DOT a una dirección ficticia. Esto es seguro porque la cantidad es cero, pero puede fallar si el remitente no tiene fondos para pagar tarifas. Para evitar gastar fondos, puedes usar una llamada de solo lectura como api.tx.balances.transfer con una cantidad cero, pero ten en cuenta que algunas cadenas rechazan transferencias de valor cero. Para una demostración segura, también puedes usar api.tx.system.remark con una pequeña nota, que solo requiere tarifas. El script está diseñado para mostrar errores, no para ejecutar una transferencia real.
const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api');
// Replace with your endpoint
const WS_URL = 'wss://rpc.polkadot.io';
async function main() {
const provider = new WsProvider(WS_URL);
const api = await ApiPromise.create({ provider });
// Create a keyring from a dev seed (for testing only)
const keyring = new Keyring({ type: 'sr25519' });
const alice = keyring.addFromUri('//Alice');
// Build a transfer of 0 DOT to a dummy address
const dummy = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'; // Alice's address for demo
const tx = api.tx.balances.transfer(dummy, 0);
// Sign the transaction
const signed = await tx.signAsync(alice);
// Submit and watch
try {
const unsub = await signed.send(({ status, events, dispatchError }) => {
if (status.isInBlock || status.isFinalized) {
console.log('Transaction included in block:', status.asInBlock.toHex());
if (dispatchError) {
console.log('Dispatch error:', decodeDispatchError(api, dispatchError));
}
events.forEach(({ event }) => {
if (api.events.system.ExtrinsicFailed.is(event)) {
console.log('Extrinsic failed:', event.data.toString());
}
});
unsub();
}
});
} catch (error) {
// This is where the 1010 error appears
console.error('Submission error:', JSON.stringify(error, null, 2));
if (error.data) {
console.log('Error data (tag):', error.data.toString());
}
}
await api.disconnect();
}
function decodeDispatchError(api, dispatchError) {
if (dispatchError.isModule) {
const { index, error } = dispatchError.asModule;
const meta = api.registry.findMetaError({ index, error });
return `${meta.section}.${meta.name}: ${meta.docs.join(' ')}`;
} else {
return dispatchError.toString();
}
}
main().catch(console.error);Salida esperada y tabla de resultados
Cuando ejecutes el script con una cuenta sin fondos, es probable que veas un error como este (la salida real varía según la cadena y el estado de la cuenta):
Si la transacción es aceptada, verás un hash de bloque y posiblemente un error de dispatch. Completa la tabla a continuación con tus propios resultados para documentar el comportamiento en tu cadena objetivo.
- Cadena/Endpoint: por ejemplo, Polkadot, Asset Hub o una parachain personalizada.
- Saldo de la cuenta: el saldo libre de la cuenta firmante.
- Nonce utilizado: el nonce que estableciste (o se autocompletó).
- Código de error: por ejemplo, 1010 o 0.
- Etiqueta de datos del error: por ejemplo, Payment, Stale, etc.
- Error de dispatch (si lo hay): por ejemplo,
Module { index: 5, error: 3 }decodificado comobalances.InsufficientBalance. - Corrección aplicada: qué cambiaste para resolver el problema.
{
"code": 1010,
"message": "Invalid Transaction",
"data": "Payment"
}
Decodificación de errores de dispatch de fallos de ejecución
Cuando un extrínseco pasa el pool pero falla durante la ejecución, el fallo no se devuelve como un error RPC. En su lugar, la transacción se incluye en un bloque y se emite el evento system.ExtrinsicFailed. El evento contiene un DispatchError que puede ser una de varias variantes:
Module { index, error }– Un error específico del pallet. Elindexse refiere al índice del pallet en el runtime, yerrores el índice de error dentro de ese pallet. Debes decodificarlos usando los metadatos del runtime.
BadOrigin– El origen (remitente) no está autorizado para llamar a esta función.
Token– Un error de token comoNoFundsoBelowMinimum.
Arithmetic– Un desbordamiento o subdesbordamiento aritmético.
Other– Un comodín para otros errores.
Para decodificar un error Module, necesitas los metadatos del runtime. @polkadot/api proporciona un helper: api.registry.findMetaError({ index, error }). Esto devuelve un objeto con section, name y docs. Por ejemplo, si obtienes Module { index: 5, error: 3 }, podría decodificarse como balances.InsufficientBalance.
El script anterior incluye una función decodeDispatchError que hace esto automáticamente. Ten en cuenta que el índice del pallet puede variar entre cadenas, así que siempre usa los metadatos de la cadena específica que estás consultando.
Para más información sobre errores de dispatch, consulta la documentación de dispatch de FRAME y los documentos para desarrolladores de Polkadot sobre transacciones.
Errores comunes y cómo evitarlos
Muchos fallos de envío de extrínsecos provienen de algunos errores recurrentes. Aquí tienes una lista de verificación para diagnosticarlos:
- Nonce incorrecto: si envías múltiples transacciones desde la misma cuenta, debes incrementar el nonce manualmente o usar
api.derive.balances.accountpara obtener el nonce actual. Usar el mismo nonce dos veces causará un errorStale.
- Fondos insuficientes para tarifas: incluso si el monto de la transferencia es cero, necesitas suficiente saldo para cubrir la tarifa de transacción. Verifica la tarifa con
api.tx.balances.transfer.estimateantes de enviar.
- Era (mortalidad) incorrecta: si estableces una
erapersonalizada que es demasiado larga, la transacción puede ser rechazada comoAncientBirthBlock. Usaapi.tx.balances.transfersin especificar una era para que la API establezca un valor seguro por defecto.
- Usar un endpoint desactualizado: si estás conectado a un nodo que está retrasado, tu transacción puede ser rechazada como
Stale. Asegúrate de estar conectado a un nodo sincronizado. OnFinality proporciona endpoints confiables; consulta nuestra página de red de Polkadot para más detalles.
- No manejar el campo
data: muchos desarrolladores solo verifican el mensaje de error y se pierden la etiqueta. Siempre registra el objeto de error completo.
- Asumir que todas las cadenas son iguales: los índices de pallet y los códigos de error varían entre Polkadot y las parachains. Siempre usa los metadatos de la cadena específica.
Para problemas a nivel de transporte como tiempos de espera o límites de velocidad, consulta nuestra guía de WebSocket y el artículo sobre límites de velocidad.
Limitaciones y compensaciones
Los métodos descritos aquí dependen del pool de transacciones del nodo y de los metadatos del runtime. Hay algunas limitaciones:
- Comportamiento específico del nodo: las verificaciones de validez del pool de transacciones pueden variar ligeramente entre versiones de nodo y configuraciones de cadena. El error
1010es estándar, pero las etiquetas exactas pueden diferir en parachains personalizadas.
- Los errores de dispatch solo son visibles después de la inclusión: si usas
author_submitExtrinsic(sin observación), no verás fallos de ejecución. Debes usarauthor_submitAndWatchExtrinsicy escuchar los eventos.
- Cambios en los metadatos: las actualizaciones del runtime pueden cambiar los índices de pallet y los códigos de error. Siempre obtén los últimos metadatos de la cadena.
- Diferencias de proveedor: algunos proveedores de RPC pueden envolver los errores de manera diferente o agregar campos adicionales. Los endpoints de OnFinality siguen el RPC estándar de Substrate, pero si usas un proveedor de terceros, prueba el formato de error. Para detalles de precios y servicio, consulta nuestras páginas de precios de RPC y servicio de API.
Esta guía no sustituye la lectura de la documentación de la cadena. Para detalles específicos de Polkadot, consulta la documentación oficial de Polkadot.
Próximos pasos y lecturas adicionales
Ahora que puedes decodificar errores 1010 y fallos de dispatch, puedes depurar tus extrínsecos de manera más efectiva. Para ir más allá:
- Explora la guía de RPC de Polkadot para una lista completa de métodos RPC.
- Aprende sobre tiempos de espera de RPC de Polkadot y latencia para optimizar tu conexión.
- Comprende los límites de velocidad y errores 429 para evitar ser limitado.
- Para problemas específicos de WebSocket, consulta la guía de RPC WebSocket de Polkadot.
- Si estás construyendo en una parachain, consulta la documentación de la propia cadena para extensiones de transacción y errores personalizados.
Si necesitas un endpoint RPC confiable, OnFinality ofrece endpoints públicos y privados; consulta nuestra página de red para más detalles. Para uso en producción, considera nuestro servicio de API para soporte dedicado.