quarkusio/quarkus · error · IllegalArgumentException

Unknown start character for json value: %s

Error message

Unknown start character for json value: %s

What it means

GreetingResource.hello (POST, application/octet-stream) throws "bad input" when the received byte array's first four bytes are not 0,1,2,3. The endpoint is a strict binary protocol check: any payload whose magic prefix differs is rejected.

Source

Thrown at core/builder/src/main/java/io/quarkus/builder/JsonReader.java:88

        switch (ch) {
            case '{':
                return readObject();
            case '[':
                return readArray();
            case '"':
                return readString();
            case 't':
                return readConstant("true", JsonBoolean.TRUE);
            case 'f':
                return readConstant("false", JsonBoolean.FALSE);
            case 'n':
                return readConstant("null", JsonNull.INSTANCE);
            default:
                if (Character.isDigit(ch) || '-' == ch) {
                    return readNumber(position);
                }
                throw new IllegalArgumentException("Unknown start character for json value: " + ch);
        }
    }

    /**
     * object
     * |---- '{' ws '}'
     * |---- '{' members '}'
     * </p>
     * members
     * |----- member
     * |----- member ',' members
     */
    private JsonValue readObject() {
        position++;

        Map<JsonString, JsonValue> members = new HashMap<>();

        while (position < length) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Send a payload whose first four bytes are 0,1,2,3 (e.g. new byte[]{0,1,2,3,...}).
  2. Ensure Content-Type is application/octet-stream so the byte[] body deserializes as raw bytes.
  3. Add a length guard (bytes.length < 4) to avoid a separate ArrayIndexOutOfBoundsException.
  4. Verify no intermediary (gzip, base64) transforms the binary body.

Example fix

// before
byte[] body = "hello".getBytes(); // fails magic check
// after
byte[] body = new byte[]{0, 1, 2, 3, 4, 5};
// Content-Type: application/octet-stream
Defensive patterns

Strategy: try-catch

Validate before calling

Response head = given().header("tenantId", tenant).get("/fruits/" + id);
if (head.getStatusCode() == 404) return; // already gone

Try / catch

Response r = given().header("tenantId", tenant).delete("/fruits/" + id);
switch (r.getStatusCode()) {
    case 204: break;                      // deleted
    case 404: break;                      // idempotent no-op
    default: throw new AssertionError("Unexpected delete status " + r.getStatusCode());
}

Prevention

When it happens

Trigger: POSTing an octet-stream body whose bytes[0..3] are not exactly 0x00 0x01 0x02 0x03 — including empty/short bodies that make bytes[i] throw ArrayIndexOutOfBounds under this error path.

Common situations: Client sending JSON/text instead of the expected binary prefix; test fixture bytes misordered; compression/encoding altering the first bytes; sending fewer than 4 bytes.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/38da897db796f261. Report an issue: GitHub.