provectus/kafka-ui · error · JsonAvroConversionException

String is not a valid json

Error message

String is not a valid json

What it means

JsonAvroConversion.convertJsonToAvro wraps Jackson's readTree in a try/catch and rethrows any JsonProcessingException as a JsonAvroConversionException with message "String is not a valid json". It means the payload string passed for JSON-to-Avro conversion could not be parsed as JSON at all, before any Avro schema validation happens. This is a client-side input format error, not a schema or Kafka problem.

Solutions

  1. Validate the payload with any JSON parser (e.g. MAPPER.readTree(json) or JSON.parse) before submitting; fix syntax errors (quote keys, use double quotes, remove trailing commas).
  2. Check that the string was not truncated or double-escaped during copy/paste or transport (e.g. nested quotes escaped twice).
  3. If generating JSON programmatically, use a serializer (Jackson ObjectMapper.writeValueAsString) instead of string concatenation.

Example fix

// before
String json = "{name: 'sensor-1', value: 42}"; // invalid: single quotes, unquoted key
Object avro = JsonAvroConversion.convertJsonToAvro(json, schema);

// after
String json = "{\"name\": \"sensor-1\", \"value\": 42}";
Object avro = JsonAvroConversion.convertJsonToAvro(json, schema);
Defensive patterns

Strategy: validation

Validate before calling

try {
  new ObjectMapper().readTree(jsonString);
} catch (JsonProcessingException e) {
  throw new IllegalArgumentException("Payload is not valid JSON: " + e.getOriginalMessage());
}

Try / catch

try {
  Object avro = JsonAvroConversion.convertJsonToAvro(json, schema);
} catch (JsonAvroConversionException e) {
  if (e.getMessage().equals("String is not a valid json")) {
    // surface a JSON syntax error to the user / reject the record
  }
}

Prevention

When it happens

Trigger: Calling convertJsonToAvro(jsonString, avroSchema) with a string that is not syntactically valid JSON, e.g. raw Java/JavaScript object literal like {name: 'x'} (unquoted keys, single quotes), trailing commas, truncated JSON, or an empty/whitespace string.

Common situations: Users pasting a record payload into the kafka-ui 'produce message' form with quotes stripped or copied from a log line; string-concatenated JSON built by hand; payloads that were serialized with Avro/protobuf and pasted as-is instead of valid JSON.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of provectus/kafka-ui@83b5a60cc0 (2026-09-08). Data as JSON: /api/errors/fedcd4f115167058. Report an issue: GitHub.

Appendix: source

Thrown at kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/jsonschema/JsonAvroConversion.java:56

import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;

// json <-> avro
public class JsonAvroConversion {

  private static final JsonMapper MAPPER = new JsonMapper();
  private static final Schema NULL_SCHEMA = Schema.create(Schema.Type.NULL);
  private static final String FORMAT = "format";
  private static final String DATE_TIME = "date-time";

  // converts json into Object that is expected input for KafkaAvroSerializer
  // (with AVRO_USE_LOGICAL_TYPE_CONVERTERS flat enabled!)
  public static Object convertJsonToAvro(String jsonString, Schema avroSchema) {
    JsonNode rootNode = null;
    try {
      rootNode = MAPPER.readTree(jsonString);
    } catch (JsonProcessingException e) {
      throw new JsonAvroConversionException("String is not a valid json");
    }
    return convert(rootNode, avroSchema);
  }

  private static Object convert(JsonNode node, Schema avroSchema) {
    return switch (avroSchema.getType()) {
      case RECORD -> {
        assertJsonType(node, JsonNodeType.OBJECT);
        var rec = new GenericData.Record(avroSchema);
        for (Schema.Field field : avroSchema.getFields()) {
          if (node.has(field.name()) && !node.get(field.name()).isNull()) {
            rec.put(field.name(), convert(node.get(field.name()), field.schema()));
          }
        }
        yield rec;
      }
      case MAP -> {
        assertJsonType(node, JsonNodeType.OBJECT);

View on GitHub (pinned to 83b5a60cc0)