bytedance/deer-flow · error · HTTPException

MCP server '{name}' sets environment variable '{env_name}',

Error message

MCP server '{name}' sets environment variable '{env_name}', which would run arbitrary code at process startup.

What it means

400 raised when a stdio MCP server definition sets an environment variable whose name (case-insensitive) is in _CODE_INJECTING_ENV_VARS — variables like PYTHONSTARTUP, NODE_OPTIONS, LD_PRELOAD, PERL5OPT that cause code to run at process startup. This closes the gap where an allowlisted binary is hijacked via its runtime's startup hooks.

Source

Thrown at backend/app/gateway/routers/mcp.py:622

        command_name = _stdio_command_name(server.command, server_name=name)
        if command_name not in allowed_commands:
            allowed = ", ".join(sorted(allowed_commands)) or "<none>"
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=(f"MCP server '{name}' uses disallowed stdio command '{command_name}'. Allowed commands: {allowed}. Configure {_MCP_STDIO_COMMAND_ALLOWLIST_ENV} to extend this list."),
            )

        exec_flag = _arbitrary_exec_arg(server.args, command=command_name)
        if exec_flag is not None:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=(f"MCP server '{name}' passes '{exec_flag}' to '{command_name}', which would run arbitrary code. Point the server at a package or module instead."),
            )

        for env_name in server.env:
            if env_name.strip().upper() in _CODE_INJECTING_ENV_VARS:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=(f"MCP server '{name}' sets environment variable '{env_name}', which would run arbitrary code at process startup."),
                )


def _mask_server_config(server: McpServerConfigResponse) -> McpServerConfigResponse:
    """Return a copy of server config with sensitive fields masked.

    Masks env values, header values, and removes OAuth secrets so they
    are not exposed through the GET API endpoint.
    """
    masked_env = {k: _MASKED_VALUE for k in server.env}
    masked_headers = {k: _MASKED_VALUE for k in server.headers}
    masked_oauth = None
    if server.oauth is not None:
        masked_oauth = server.oauth.model_copy(
            update={
                "client_secret": None,

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Remove the code-injecting variable from the server's env block
  2. Achieve the effect differently: set runtime options via launcher args, or configure the server package itself
  3. If a variable is genuinely required and safe, ask the operator to run that server outside the API-managed path

Example fix

# before
{"command": "npx", "args": ["-y", "srv"], "env": {"NODE_OPTIONS": "--require ./preload.js"}}
# after
{"command": "npx", "args": ["-y", "srv"], "env": {"SRV_CONFIG_PATH": "/etc/srv/config.json"}}
Defensive patterns

Strategy: validation

Validate before calling

const CODE_INJECTING = new Set(['PYTHONSTARTUP','NODE_OPTIONS','LD_PRELOAD','LD_AUDIT','PERL5OPT','RUBYOPT','JAVA_TOOL_OPTIONS']);
function assertSafeEnv(env: Record<string, string>) { for (const k of Object.keys(env)) if (CODE_INJECTING.has(k.trim().toUpperCase())) throw new Error(`env var ${k} can inject startup code`); }

Type guard

function isCodeInjectingEnvName(name: string): boolean { return ['PYTHONSTARTUP','NODE_OPTIONS','LD_PRELOAD','PERL5OPT','RUBYOPT','JAVA_TOOL_OPTIONS'].includes(name.trim().toUpperCase()); }

Try / catch

null

Prevention

When it happens

Trigger: Submitting env: {"NODE_OPTIONS": "--require ./hook.js"} or {"PYTHONSTARTUP": "script.py"} with a stdio server; copying a Docker/env-file setup that relies on these tuning variables into MCP config.

Common situations: Legitimate-looking performance/debug tuning (NODE_OPTIONS=--max-old-space-size is still rejected as a class); hardening bypass attempts; env blocks copied from local development shells.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/870023ea28c9864a. Report an issue: GitHub.