boto3: Parameter validation failed (ParamValidationError)

TL;DR — botocore.exceptions.ParamValidationError se lanza en tu máquina, antes de que se envíe cualquier solicitud — los argumentos que pasaste no coinciden con la forma esperada de la operación. En código de DynamoDB casi siempre es una confusión entre client y resource: el client de bajo nivel quiere DynamoDB-JSON ({'S': 'abc'}, números como cadenas), el resource Table quiere tipos nativos de Python. Ajusta el estilo a la interfaz que estás llamando.

Qué significa

botocore.exceptions.ParamValidationError: Parameter validation failed:
Invalid type for parameter Item.price.N, value: 42, type: <class 'int'>,
valid types: <class 'str'>

botocore valida cada llamada contra el modelo de API del servicio antes de firmarla. Un fallo aquí no es un ClientError — DynamoDB nunca vio la solicitud — así que except ClientError no lo capturará, y no ocurrió ningún viaje de red. El mensaje nombra la ruta exacta del parámetro que falló y el tipo que esperaba.

Por qué ocurre

  • Valores nativos de Python pasados al client de bajo nivelboto3.client('dynamodb') habla DynamoDB JSON crudo: cada atributo es un mapa etiquetado por tipo y los valores N son cadenas ({'N': '42'}, no 42).
  • DynamoDB-JSON pasado al resource Table — la confusión inversa: boto3.resource('dynamodb').Table(...) espera valores planos de Python y hace el marshalling por ti.
  • Objetos de condición donde se espera una cadena — el KeyConditionExpression del paginador de query acepta una expresión de cadena; los objetos Key('pk').eq(...) fallan la validación ahí.
  • Un nombre de parámetro con errata o no soportado — las claves desconocidas fallan la validación; un botocore desactualizado también puede rechazar parámetros añadidos a la API después de su modelo empaquetado.

Cómo solucionarlo

  1. Elige una interfaz y usa su estilo de tipos de forma consistente:

    # Table resource — native Python types
    table = boto3.resource('dynamodb').Table('orders')
    table.put_item(Item={'pk': 'ORDER#1', 'price': Decimal('42')})
    
    # Low-level client — DynamoDB-JSON, numbers as strings
    client = boto3.client('dynamodb')
    client.put_item(TableName='orders',
                    Item={'pk': {'S': 'ORDER#1'}, 'price': {'N': '42'}})
  2. Usa expresiones de cadena con los paginadoresKeyConditionExpression='pk = :p' más ExpressionAttributeValues, o pagina el resource Table manualmente con LastEvaluatedKey.

  3. Lee la ruta del parámetro en el mensajeItem.price.N te dice exactamente qué atributo y qué etiqueta de tipo falló; arregla ese campo en lugar de adivinar.

  4. Actualiza botocore para los fallos "Unknown parameter" — si el parámetro es real pero tu modelo de validación es anterior a él, pip install -U boto3 botocore.

  5. Captúralo por separado de los errores de servicio:

    from botocore.exceptions import ClientError, ParamValidationError
    try:
        client.put_item(**kwargs)
    except ParamValidationError as e:   # local: fix the call
        ...
    except ClientError as e:            # remote: DynamoDB rejected it
        ...

Escribir DynamoDB-JSON a mano es donde estas etiquetas de tipo se tuercen — el conversor de DynamoDB JSON traduce entre JSON nativo y el formato de cable etiquetado por tipo, y la app de escritorio DynoTable edita Items con el marshalling gestionado por ti.

Errores relacionados

Referencias

Verificado por última vez el 2026-07-13 contra la documentación oficial de AWS enlazada arriba.

Trabaja con DynamoDB sin la Consola

DynoTable es un cliente de escritorio rápido para DynamoDB: explora tablas, ejecuta consultas estilo SQL y edita Items localmente.