apache/iceberg · error · JsonToMapException

record value is not a string, use StringConverter

Error message

record value is not a string, use StringConverter

What it means

JsonToMapTransform requires the record value to be a raw JSON string. If record.value() is not a String, process() throws JsonToMapException('record value is not a string, use StringConverter'), telling you to convert the value to a String before this transform.

Solutions

  1. Insert org.apache.kafka.connect.transforms.Cast or a StringConverter step before JsonToMapTransform
  2. Set the value converter to a String-producing one (e.g. StringConverter or JsonConverter with schemas.enable=false plus cast to string)
  3. Ensure the source connector emits String values
  4. Use a different transform appropriate for Struct/Map values

Example fix

// before
"transforms.json.type": "org.apache.iceberg.connect.transforms.JsonToMapTransform$Value"
// after (add a cast first)
"transforms.cast.type": "org.apache.kafka.connect.transforms.Cast$Value",
"transforms.cast.spec": "string",
"transforms.json.type": "org.apache.iceberg.connect.transforms.JsonToMapTransform$Value"
Defensive patterns

Strategy: type-guard

Validate before calling

boolean jsonTransformCompatible(SinkRecord r) {
  return r.value() == null || r.value() instanceof String;
}

Type guard

boolean isStringValue(SinkRecord r) {
  return r.value() instanceof String;
}

Try / catch

try {
  return transform.apply(record);
} catch (JsonToMapException e) {
  LOG.error("Value is not a String; prepend Cast$Value spec=string", e);
  throw e;
}

Prevention

When it happens

Trigger: Inserting JsonToMapTransform after a converter produced a Map, byte[], Struct, or other non-String value — e.g. JsonConverter schemas.enable=true producing a Struct, or AvroConverter producing a Struct.

Common situations: Chaining SMTs where an upstream converter/transform already decoded the JSON; using ByteArrayConverter (value is byte[]); records produced by schema-based source connectors.

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/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/ed8a4e5036a52ab0. Report an issue: GitHub.

Appendix: source

Thrown at kafka-connect/kafka-connect-transforms/src/main/java/org/apache/iceberg/connect/transforms/JsonToMapTransform.java:73

  private static final Schema JSON_MAP_SCHEMA =
      SchemaBuilder.struct()
          .field(
              ALL_JSON_SCHEMA_FIELD,
              SchemaBuilder.map(Schema.STRING_SCHEMA, Schema.STRING_SCHEMA).optional().build())
          .build();

  @Override
  public R apply(R record) {
    if (record.value() == null) {
      return record;
    } else {
      return process(record);
    }
  }

  private R process(R record) {
    if (!(record.value() instanceof String)) {
      throw new JsonToMapException("record value is not a string, use StringConverter");
    }

    String json = (String) record.value();
    JsonNode obj;

    try {
      obj = MAPPER.readTree(json);
    } catch (Exception e) {
      throw new JsonToMapException(
          String.format(
              "record.value is not valid json for record.value: %s", collectRecordDetails(record)),
          e);
    }

    if (!(obj instanceof ObjectNode)) {
      throw new JsonToMapException(
          String.format(
              "Expected json object for record.value after parsing: %s",

View on GitHub (pinned to 86d9c8fc54)