nanocoai/nanoclaw · error · Error
url query parameter "${key}" looks like a credential; use On
Error message
url query parameter "${key}" looks like a credential; use OneCLI for authentication What it means
A query parameter key in the MCP url matches a credential-looking pattern (SECRET_QUERY_KEY_RE after camelCase normalization), e.g. token, apiKey, api_key, secret. Like embedded userinfo, this is rejected to keep secrets out of config files and logs — auth belongs in OneCLI.
Source
Thrown at src/container-config.ts:166
if (input.args !== undefined || input.env !== undefined || input.cwd !== undefined) {
throw new Error('args, env, and cwd are only valid with command');
}
let parsed: URL;
try {
parsed = new URL(url);
} catch (err) {
throw new Error('url must be a valid HTTP(S) URL', { cause: err });
}
const loopback = ['localhost', '127.0.0.1', '[::1]', 'host.docker.internal'].includes(parsed.hostname);
if (parsed.protocol !== 'https:' && !(parsed.protocol === 'http:' && loopback)) {
throw new Error('url must use HTTPS (plain HTTP is allowed only for localhost and host.docker.internal)');
}
if (parsed.username || parsed.password || parsed.hash) {
throw new Error('url must not contain credentials or fragments; use OneCLI for authentication');
}
for (const key of parsed.searchParams.keys()) {
if (SECRET_QUERY_KEY_RE.test(key.replace(CAMEL_SPLIT_RE, '$1_$2'))) {
throw new Error(`url query parameter "${key}" looks like a credential; use OneCLI for authentication`);
}
}
const headers = parseStringRecord(input.headers, 'headers');
return {
type: 'http',
url,
...(headers === undefined ? {} : { headers }),
...(instructions === undefined ? {} : { instructions }),
};
}
if (command === undefined) throw new Error('Provide exactly one of command or url');
if (input.headers !== undefined) throw new Error('headers is only valid with url');
const args = input.args ?? [];
if (!Array.isArray(args) || !args.every((arg) => typeof arg === 'string')) {
throw new Error('args must be a JSON array of strings');
}
const env = parseStringRecord(input.env, 'env') ?? {};View on GitHub (pinned to 294ef2aee8)
Solutions
- Move the credential into OneCLI and let it inject the appropriate header at request time
- If the parameter is genuinely not a secret, rename it to something that doesn't match token/key/secret patterns
- Ask the MCP server operator for a header-based auth scheme
Example fix
// before
{"url":"https://mcp.example.com/mcp?apiKey=abc123"}
// after
{"url":"https://mcp.example.com/mcp"} // key stored in OneCLI Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(entry.url); for (const k of u.searchParams.keys()) if (/token|key|secret|pass/i.test(k)) throw new UserError(`move ${k} to OneCLI`); Type guard
const urlQueryLooksSafe = (s: string) => { const u = new URL(s); return ![...u.searchParams.keys()].some(k => /token|key|secret|pass/i.test(k)); }; Try / catch
catch (err) { if (err.message.includes('looks like a credential')) stripQueryAndUseVault(); else throw err; } Prevention
- Avoid ?token=/?key= style query auth entirely
- Rename genuinely non-secret params away from token/key/secret words
When it happens
Trigger: urls like https://mcp.example.com/mcp?token=abc123 or ?apiKey=XYZ in an http MCP server entry.
Common situations: Vendor docs that put the API key in a query string; copy-pasting a browser-authenticated URL; Sentry/GitHub-style ?token= patterns.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- url must not contain credentials or fragments; use OneCLI fo
- url must use HTTPS (plain HTTP is allowed only for localhost
- cwd escapes the plugin root
- ${settingsFile} hooks.SessionStart must be an array
- --name is required
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/9529bf583fda7a5c.
Report an issue: GitHub.