eclipse-vertx/vert.x · error · IllegalArgumentException

a header value contains a prohibited character '127': <seq>

Error message

a header value contains a prohibited character '127': <seq>

What it means

Inside validateValueChar, the per-character state machine for header values rejects DEL (0x7F) outright with IllegalArgumentException. This is the fine-grained path reached after a non-printable char triggers deeper validation; it guarantees no prohibited character ends up on the wire.

Source

Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/HttpUtils.java:732

      // we already expect the very-first character to be non-printable
      int state = validateValueChar(seq, NO_CR_LF_STATE, seq.charAt(index));
      for (int i = index + 1; i < seq.length(); i++) {
        state = validateValueChar(seq, state, seq.charAt(i));
      }
      if (state != NO_CR_LF_STATE) {
        throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq);
      }
  }

  private static int validateValueChar(CharSequence seq, int state, char ch) {
    /*
     * State:
     * 0: Previous character was neither CR nor LF
     * 1: The previous character was CR
     * 2: The previous character was LF
     */
    if (ch == 0x7F) {
      throw new IllegalArgumentException("a header value contains a prohibited character '127': " + seq);
    }
    if ((ch & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) {
      // this is a rare scenario
      validateNonPrintableCtrlChar(seq, ch);
      // this can include LF and CR as they are non-printable characters
      if (state == NO_CR_LF_STATE) {
        // Check the CRLF (HT | SP) pattern
        switch (ch) {
          case '\r':
            return CR_STATE;
          case '\n':
            return LF_STATE;
        }
        return NO_CR_LF_STATE;
      }
    }
    if (state != NO_CR_LF_STATE) {
      // this is a rare scenario

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Reject or sanitize values containing 0x7F before calling headers.set/add
  2. Validate inputs at the boundary (decode/sanitize once) rather than at each header write
  3. Encode opaque/binary data (Base64) instead of passing raw chars

Example fix

// before
String v = new String(bytes, StandardCharsets.US_ASCII);
request.putHeader("X-Data", v);
// after
String v = Base64.getEncoder().encodeToString(bytes);
request.putHeader("X-Data", v);
Defensive patterns

Strategy: validation

Validate before calling

boolean hasProhibitedChar(CharSequence v) {
  for (int i = 0; i < v.length(); i++) {
    char c = v.charAt(i);
    if (c == 0x7F || (c < 0x20 && c != 0x09)) return true;
  }
  return false;
}
if (hasProhibitedChar(value)) value = sanitize(value);

Try / catch

try {
  request.putHeader(name, value);
} catch (IllegalArgumentException e) {
  request.putHeader(name, value.replaceAll("[\\u0000-\\u001F\\u007F]", ""));
}

Prevention

When it happens

Trigger: Setting an HTTP header value that contains U+007F, validated by the state machine in validateValueChar (often entered via validateSequenceHeaderValue after a control char is seen).

Common situations: Header values assembled from binary or escaped data where a literal DEL survives; fuzzed or adversarial input passed into outgoing headers.

Related errors


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/fc2becc8936c6a38. Report an issue: GitHub.