nanocoai/nanoclaw · critical · deniedByPolicy
credential value in contributed env '${key}' on ${container.
Error message
credential value in contributed env '${key}' on ${container.role} What it means
A contributedEnv entry's VALUE looks like a real credential. contributedEnv is the sanctioned lane where credential-shaped NAMES are fine (a provider registering ANTHROPIC_AUTH_TOKEN=placeholder for the proxy to overwrite is the intended pattern), so only the name is exempt — the value check still applies, because credential values have no sanctioned channel from anyone; real material rides mounts by reference.
Source
Thrown at src/drivers/types.ts:490
throw deniedByPolicy(`identity-material mount ${mount.hostPath} invalid on role ${container.role}`);
}
if (!mountAllowed(mount, spec, policy)) {
throw deniedByPolicy(`mount ${mount.hostPath} violates class ${mount.class} scope ${mount.groupScope}`);
}
}
for (const [key, value] of Object.entries(container.env)) {
if (isSecretShaped(key, value)) {
throw deniedByPolicy(`secret-shaped env '${key}' on ${container.role}`);
}
}
for (const [key, value] of Object.entries(container.contributedEnv ?? {})) {
// The sanctioned lane: credential-shaped NAMES are its purpose — a
// provider registering `ANTHROPIC_AUTH_TOKEN=placeholder` for the proxy
// to overwrite is the pattern working as intended, and the name check
// alone denies every such install. Credential VALUES have no sanctioned
// channel, from anyone: real material rides mounts by reference.
if (looksLikeCredential(value)) {
throw deniedByPolicy(`credential value in contributed env '${key}' on ${container.role}`);
}
}
}
}
/**
* The canonical absolute form the mount rules require: rooted, and free of
* empty, '.' and '..' segments — so the string a prefix rule judges is the
* path the runtime mounts, and a trusted root cannot be escaped lexically.
*/
function hostPathCanonical(hostPath: string): boolean {
if (!hostPath.startsWith('/')) return false;
const segments = hostPath.split('/').slice(1);
return segments.every((segment) => segment !== '' && segment !== '.' && segment !== '..');
}
/**
* The invariant is that no credential VALUE rides in the environment — not thatView on GitHub (pinned to 294ef2aee8)
Solutions
- Use an unmistakable placeholder value (e.g. 'placeholder', '__PROXY_FILLED__') — the pattern is that the proxy overwrites it at runtime, so the literal bytes never matter.
- If a real credential must reach the container, mount it by reference with the right mount class instead of contributing it as a value.
- Adjust test fixture generators to emit short, obviously-fake strings rather than token-shaped randomness.
Example fix
// before
contributedEnv: { ANTHROPIC_AUTH_TOKEN: 'sk-ant-api03-a1b2c3d4e5f6...' }
// after
contributedEnv: { ANTHROPIC_AUTH_TOKEN: 'placeholder' } Defensive patterns
Strategy: validation
Validate before calling
for (const c of spec.containers)
for (const [k, v] of Object.entries(c.contributedEnv ?? {}))
if (looksLikeCredential(v)) throw new Error(`contributedEnv '${k}' carries a credential-shaped value; use a placeholder`); Type guard
function isSafePlaceholder(v: string): boolean {
return !looksLikeCredential(v); // short, non-token-shaped literals Prevention
- Use fixed, obviously-fake placeholders ('placeholder', '__PROXY__') in contributedEnv.
- Test fixtures: avoid random hex/base64 generators long enough to look like tokens.
- Remember only the NAME is exempt in contributedEnv — the value check always applies.
When it happens
Trigger: A plugin/provider contribution includes contributedEnv like { ANTHROPIC_AUTH_TOKEN: 'sk-ant-...' } — a plausible-looking placeholder that actually matches the credential-value heuristics (looksLikeCredential). Also any contributed value that happens to be a long token-shaped string.
Common situations: Writing a provider integration and putting a real or realistic token in the 'placeholder' slot; test fixtures generating random hex strings long enough to trip the credential heuristic; copying an env block from a working shell session into the contribution.
Related errors
- secret-shaped env '${key}' on ${container.role}
- ${settingsFile} hooks.SessionStart must be an array
- mount ${mount.hostPath} must be classed ${required}, not ${m
- install-surface mount ${mount.hostPath} must be ro
- identity-material mount ${mount.hostPath} invalid on role ${
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/1a41164b1c15e758.
Report an issue: GitHub.