headroomlabs-ai/headroom · warning
fixture {} declares transform={} but lives under {}
Error message
fixture {} declares transform={} but lives under {} What it means
The second 404 gate in require_loopback (loopback_guard.py:216): the TCP peer is loopback but the Host header does not name a loopback host. This is the DNS-rebinding defense — an attacker's DNS name (e.g. attacker.com) resolves to 127.0.0.1 so the peer IP passes, but the browser-sent Host header betrays the non-local origin, and the request is 404'd.
Source
Thrown at crates/headroom-parity/src/lib.rs:100
/// Load every `*.json` fixture under `dir/<transform>/`.
pub fn load_fixtures_for(dir: &Path, transform: &str) -> Result<Vec<(PathBuf, Fixture)>> {
let root = dir.join(transform);
if !root.exists() {
return Ok(Vec::new());
}
let mut out = Vec::new();
for entry in fs::read_dir(&root).with_context(|| format!("reading {}", root.display()))? {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) != Some("json") {
continue;
}
let bytes = fs::read(&path).with_context(|| format!("reading {}", path.display()))?;
let fixture: Fixture = serde_json::from_slice(&bytes)
.with_context(|| format!("parsing fixture {}", path.display()))?;
if fixture.transform != transform {
bail!(
"fixture {} declares transform={} but lives under {}",
path.display(),
fixture.transform,
transform
);
}
out.push((path, fixture));
}
Ok(out)
}
/// Aggregate report of one comparator run.
#[derive(Debug, Default)]
pub struct Report {
pub matched: usize,
pub diffed: Vec<(PathBuf, String, String)>,
pub skipped: Vec<(PathBuf, String)>,
}View on GitHub (pinned to 322425c43b)
Solutions
- Use http://127.0.0.1:PORT or http://localhost:PORT directly
- Remove Host header overrides from your curl command
- If a name is required, use a name that resolves to a loopback literal and ensure the Host header itself is localhost or a 127.x address
Example fix
# before curl -H 'Host: mybox.local' http://127.0.0.1:8080/debug/tasks # after curl http://127.0.0.1:8080/debug/tasks
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
host = urlparse(url).hostname
assert host in ("127.0.0.1", "localhost") or host.startswith("127."), f"Host {host} will be 404'd by the loopback guard" Prevention
- Always call local admin endpoints via 127.0.0.1, not a hostname
- Remove Host overrides from curl commands targeting debug routes
When it happens
Trigger: Browsing to http://your-hostname:8080/debug/... where the hostname resolves to 127.0.0.1 but is not 'localhost'/an ip-literal; tools that set a custom Host header (e.g. curl -H 'Host: mybox.local'); a rebinding attack where a public domain flips to 127.0.0.1.
Common situations: Using a machine's hostname or a LAN DNS name that happens to resolve to loopback; curl with explicit Host overrides; legitimate dashboards accessed via a named vhost.
Related errors
- recommendations TOML parse error: {0}
- comparator cache_aligner not implemented (Phase 0)
- comparator ccr not implemented (Phase 0)
- sha256 mismatch for {path.name}: expected {expected}, got {g
- Invalid {TRUSTED_DASHBOARD_CLIENT_CIDRS_ENV} entry: {exc}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/6c0663e9481190ac.
Report an issue: GitHub.