openai/codex · error · anyhow::Error

network.mitm_hooks[{hook_index}].host must not be empty

Error message

network.mitm_hooks[{hook_index}].host must not be empty

What it means

validate_mitm_hook_config checks that each MITM hook's host is non-empty. In practice this branch is shadowed: normalize_hook_host runs earlier in the same loop (line 183) and already fails with 'host must not be empty' under the context 'invalid network.mitm_hooks[i].host' whenever a host normalizes to nothing, so the line-223 check is a defensive re-check that a successfully normalized (hence non-empty) host cannot trigger.

Source

Thrown at codex-rs/network-proxy/src/mitm_hook.rs:223

            let _ = body;
            return Err(anyhow!(
                "network.mitm_hooks[{hook_index}].match.body is reserved for a future release and is not yet supported"
            ));
        }

        validate_query_constraints(&hook.matcher.query)
            .with_context(|| format!("invalid network.mitm_hooks[{hook_index}].match.query"))?;
        validate_header_constraints(&hook.matcher.headers)
            .with_context(|| format!("invalid network.mitm_hooks[{hook_index}].match.headers"))?;
        validate_strip_request_headers(&hook.actions.strip_request_headers).with_context(|| {
            format!("invalid network.mitm_hooks[{hook_index}].actions.strip_request_headers")
        })?;
        validate_injected_headers(&hook.actions.inject_request_headers).with_context(|| {
            format!("invalid network.mitm_hooks[{hook_index}].actions.inject_request_headers")
        })?;

        if host.is_empty() {
            return Err(anyhow!(
                "network.mitm_hooks[{hook_index}].host must not be empty"
            ));
        }
    }

    Ok(())
}

pub(crate) fn compile_mitm_hooks(config: &NetworkProxyConfig) -> Result<MitmHooksByHost> {
    compile_mitm_hooks_with_resolvers(
        config,
        |name| env::var(name).ok(),
        |path| {
            let value = fs::read_to_string(path.as_path()).with_context(|| {
                format!("failed to read secret file {}", path.as_path().display())
            })?;
            Ok(value.trim().to_string())
        },

View on GitHub (pinned to 339751715c)

Solutions

  1. Set host to an exact hostname, e.g. host = "api.example.com"
  2. If config is templated, fail the substitution step when a host renders empty
  3. Expect the sibling normalize_hook_host message with the 'invalid network.mitm_hooks[i].host' context — use its index to find the bad entry

Example fix

# before
[[network.mitm_hooks]]
host = ""

# after
[[network.mitm_hooks]]
host = "api.example.com"
Defensive patterns

Strategy: validation

Validate before calling

// Rust — before compiling hooks
for (i, hook) in config.mitm_hooks.iter().enumerate() {
    if hook.host.trim().is_empty() {
        return Err(anyhow!("network.mitm_hooks[{i}].host is empty"));
    }
}

Type guard

fn hook_host_is_set(hook: &MitmHookConfig) -> bool {
    !hook.host.trim().is_empty()
}

Try / catch

match validate_mitm_hook_config(&config) {
    Ok(()) => {}
    Err(err) => eprintln!("{err:#}"), // 'invalid network.mitm_hooks[0].host: host must not be empty'
}

Prevention

When it happens

Trigger: A [[network.mitm_hooks]] entry with host = "" or a whitespace-only host. The error actually observed for this condition is 'invalid network.mitm_hooks[i].host: host must not be empty' from normalize_hook_host; the line-223 message can only appear if the earlier normalization passed an empty host, which its own contract prevents.

Common situations: Template config with an unfilled host placeholder; variable substitution rendering an empty string; host key omitted entirely (serde default is the empty string).

Related errors


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/35ae7af0c4d0c184. Report an issue: GitHub.