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
- Sanitize/strip CR and LF from any header value derived from untrusted or templated input before building the headers map.
- If a value legitimately spans multiple lines, encode it (e.g. base64 or JSON) into a single-line header.
- Audit where the headers map is constructed — ensure values come from trusted, single-line sources.
- 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
- Never interpolate untrusted/workflow input directly into header values.
- Strip trailing newlines from secrets loaded from files/env.
- Encode multi-line values (base64/JSON) rather than placing them raw in a header.
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
- Refusing to forward credentials across an MCP redirect
- MCP response exceeds the 10 MiB payload limit
- plannerContext header '${name}' contains CR/LF — rejected to
- Skill package exceeds max uncompressed size of {maxUncompres
- Invalid skill package path: {name}
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/fc4260f6d568f8d3.
Report an issue: GitHub.