conductor-oss/conductor · error · IllegalArgumentException

MCP headers must not contain CR or LF characters

Error message

MCP headers must not contain CR or LF characters

What it means

Thrown by addHeaders when any header name or value contains a CR (\r) or LF (\n) character. This is a CRLF-injection / header-injection guard: injecting a newline into a header value could let an attacker smuggle additional headers or start a new request. It is a deliberate security check, not a format preference.

Source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java:415

                request = request.newBuilder().url(target).build();
            }
        }
        throw new RuntimeException("MCP server exceeded the redirect limit");
    }

    private void addHeaders(Request.Builder builder, Map<String, String> headers) {
        if (headers == null || headers.isEmpty()) {
            return;
        }
        headers.forEach(
                (name, value) -> {
                    if (name == null
                            || value == null
                            || name.indexOf('\r') >= 0
                            || name.indexOf('\n') >= 0
                            || value.indexOf('\r') >= 0
                            || value.indexOf('\n') >= 0) {
                        throw new IllegalArgumentException(
                                "MCP headers must not contain CR or LF characters");
                    }
                    builder.header(name, value);
                });
    }

    private boolean hasSensitiveHeaders(Request request) {
        return request.header("Authorization") != null
                || request.header("Cookie") != null
                || request.header("Proxy-Authorization") != null;
    }

    private boolean isSameOrigin(String firstUrl, String secondUrl) {
        okhttp3.HttpUrl first = okhttp3.HttpUrl.parse(firstUrl);
        okhttp3.HttpUrl second = okhttp3.HttpUrl.parse(secondUrl);
        return first != null
                && second != null
                && first.scheme().equalsIgnoreCase(second.scheme())

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Sanitize/strip CR and LF from any header value derived from untrusted or templated input before building the headers map.
  2. If a value legitimately spans multiple lines, encode it (e.g. base64 or JSON) into a single-line header.
  3. Audit where the headers map is constructed — ensure values come from trusted, single-line sources.
  4. Strip a trailing newline from secrets loaded from files/env.

Example fix

// before
Map<String,String> headers = Map.of("Authorization", "Bearer " + userInput);
// after
String safe = userInput == null ? "" : userInput.replaceAll("[\\r\\n]", "");
if (!safe.equals(userInput)) throw new IllegalArgumentException("header value contains CR/LF");
Map<String,String> headers = Map.of("Authorization", "Bearer " + safe);
Defensive patterns

Strategy: validation

Validate before calling

// Reject CR/LF in any header before building the map.
Map<String,String> safeHeaders = new LinkedHashMap<>();
headers.forEach((k, v) -> {
    if (k == null || v == null
            || k.indexOf('\r') >= 0 || k.indexOf('\n') >= 0
            || v.indexOf('\r') >= 0 || v.indexOf('\n') >= 0) {
        throw new IllegalArgumentException("Header contains CR/LF: " + k);
    }
    safeHeaders.put(k, v);
});

Try / catch

try {
    mcpService.callTool(serverUrl, toolName, arguments, headers);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("CR or LF")) {
        // sanitize header source (often a templated/untrusted value) and retry
    }
    throw e;
}

Prevention

When it happens

Trigger: The headers map passed to listTools/callTool contains a name or value with \r or \n. This happens when header values are built from untrusted/user input without sanitization (e.g. a workflow parameter interpolated into an Authorization or X- header), or when a value accidentally includes a trailing newline.

Common situations: Workflow/task input templated into an MCP header without escaping; secrets read from a file that include a trailing newline; multi-line values mistakenly placed in a single header; attempted header injection via user-controlled config.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/fc4260f6d568f8d3. Report an issue: GitHub.