apache/cassandra · error · MarshalException

Expected a map, but got a

Error message

Expected a map, but got a %s: %s

What it means

UserType.fromJSONObject expects the parsed JSON for a UDT value to be a JSON object (java.util Map). If it is any other type — after optionally decoding a string via JsonUtils.decodeJson — this MarshalException is thrown with the actual runtime type.

Solutions

  1. Supply the UDT value as a JSON object with field names as keys
  2. If passing a string, ensure it is valid JSON object syntax like '{"street":"..."}' so decodeJson succeeds
  3. Inspect the payload type at runtime and construct a Map<String,Object> manually

Example fix

// before
term = udt.fromJSONObject(Arrays.asList("a","b")); // Expected a map
// after
Map<String,Object> value = new HashMap<>();
value.put("field1", "a");
term = udt.fromJSONObject(value);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(parsed instanceof Map) && !(parsed instanceof String)) throw new IllegalArgumentException("UDT value must be a JSON object");

Type guard

boolean isUdtObject(Object o) { return (o instanceof Map) || (o instanceof String && ((String) o).trim().startsWith("{")); }

Try / catch

try { term = udt.fromJSONObject(parsed); } catch (MarshalException e) { throw new BadRequestException("UDT field requires a JSON object"); }

Prevention

When it happens

Trigger: Passing a JSON array, scalar, or non-JSON string that fails decodeJson to UserType.fromJSONObject; passing a JSON-encoded UDT string with invalid embedded syntax.

Common situations: UDT column bound from a list or scalar in the application; JSON body field mis-typed as array instead of object; nested UDT where the inner value was quoted wrongly.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/f9fff6a097f5da2b. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/db/marshal/UserType.java:299

            ByteBuffer path = cell.path().get(0);
            nameComparator().validate(path);
            Short fieldPosition = nameComparator().getSerializer().deserialize(path);
            fieldType(fieldPosition).validate(cell.value(), cell.accessor());
        }
        else
        {
            validate(cell.value(), cell.accessor());
        }
    }

    @Override
    public Term fromJSONObject(Object parsed) throws MarshalException
    {
        if (parsed instanceof String)
            parsed = JsonUtils.decodeJson((String) parsed);

        if (!(parsed instanceof Map))
            throw new MarshalException(String.format(
                    "Expected a map, but got a %s: %s", parsed.getClass().getSimpleName(), parsed));

        Map<String, Object> map = (Map<String, Object>) parsed;

        JsonUtils.handleCaseSensitivity(map);

        List<Term> terms = new ArrayList<>(types.size());

        Set keys = map.keySet();
        assert keys.isEmpty() || keys.iterator().next() instanceof String;

        int foundValues = 0;
        for (int i = 0; i < types.size(); i++)
        {
            Object value = map.get(stringFieldNames.get(i));
            if (value == null)
            {
                terms.add(Constants.NULL_VALUE);

View on GitHub (pinned to 88fd0f6a0e)