prestodb/presto · error · JsonCastException

INVALID_CAST_ARGUMENT

INVALID_CAST_ARGUMENT

Error message

Unexpected token when cast to %s: %s

What it means

currentTokenAsVarchar converts the current Jackson parser token to a Presto VARCHAR during JSON-to-SQL casting. Only VALUE_STRING, VALUE_NUMBER, VALUE_TRUE and VALUE_FALSE are convertible; any other token (null, START_OBJECT, START_ARRAY, FIELD_NAME, NOT_AVAILABLE, embedded objects) throws JsonCastException (INVALID_CAST_ARGUMENT) naming VARCHAR and the offending token text.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/util/JsonUtil.java:702

        switch (parser.currentToken()) {
            case VALUE_NULL:
                return null;
            case VALUE_STRING:
            case FIELD_NAME:
                return Slices.utf8Slice(parser.getText());
            case VALUE_NUMBER_FLOAT:
                // Avoidance of loss of precision does not seem to be possible here because of Jackson implementation.
                return DoubleOperators.castToVarchar(parser.getDoubleValue());
            case VALUE_NUMBER_INT:
                // An alternative is calling getLongValue and then BigintOperators.castToVarchar.
                // It doesn't work as well because it can result in overflow and underflow exceptions for large integral numbers.
                return Slices.utf8Slice(parser.getText());
            case VALUE_TRUE:
                return BooleanOperators.castToVarchar(true);
            case VALUE_FALSE:
                return BooleanOperators.castToVarchar(false);
            default:
                throw new JsonCastException(format("Unexpected token when cast to %s: %s", StandardTypes.VARCHAR, parser.getText()));
        }
    }

    public static Long currentTokenAsBigint(JsonParser parser)
            throws IOException
    {
        switch (parser.currentToken()) {
            case VALUE_NULL:
                return null;
            case VALUE_STRING:
            case FIELD_NAME:
                return VarcharOperators.castToBigint(Slices.utf8Slice(parser.getText()));
            case VALUE_NUMBER_FLOAT:
                return DoubleOperators.castToLong(parser.getDoubleValue());
            case VALUE_NUMBER_INT:
                return parser.getLongValue();
            case VALUE_TRUE:
                return BooleanOperators.castToBigint(true);

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Check the JSON value is a scalar (string, number, or boolean) before casting to VARCHAR; handle nulls with json_typeof or JSON_QUERY wrappers.
  2. Coerce objects/arrays with json_format(CAST(col AS JSON)) instead of direct CAST to VARCHAR.
  3. Fix the upstream producer so the field is always a JSON string, or use TRY_CAST to get NULL instead of a query failure.

Example fix

// before
SELECT CAST(json_extract(j, '$.details') AS VARCHAR) FROM t; -- fails when details is an object
// after
SELECT TRY_CAST(json_extract(j, '$.details') AS VARCHAR) FROM t; -- NULL instead of error, or guard with json_typeof
Defensive patterns

Strategy: try-catch

Validate before calling

SELECT json_typeof(json_extract(j, '$.path'))
FROM t; -- must be 'string', 'number', or 'boolean' before CAST to VARCHAR

Type guard

boolean isVarcharCastable(String jsonValue) {
    String t = jsonTypeOf(jsonValue); // e.g. via json_typeof
    return "string".equals(t) || "number".equals(t) || "boolean".equals(t);
}

Try / catch

try {
    v = castJsonToVarchar(j);
} catch (PrestoException e) {
    if (e.getErrorCode().getName().equals("INVALID_CAST_ARGUMENT") && e.getMessage().contains("varchar")) {
        v = jsonFormat(j); // serialize objects/arrays instead
    } else { throw e; }
}

Prevention

When it happens

Trigger: Casting a JSON value that is an object, array, or null to VARCHAR — e.g. CAST(JSON 'null' AS VARCHAR), CAST(JSON '{"a":1}' AS VARCHAR), or a JSON path/unnest landing on a non-scalar token.

Common situations: Ingesting semi-structured JSON where a field expected to be a string is sometimes an object/array/null; schema drift between producer and consumer; using json_extract with an implicit cast when the path hits a container.

Understand the failure class

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/499056875663e620. Report an issue: GitHub.