{"record":{"id":"11997e7b347057ca","repo":"nanocoai/nanoclaw","slug":"headers-is-only-valid-with-url","errorCode":null,"errorMessage":"headers is only valid with url","messagePattern":"headers is only valid with url","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/container-config.ts","lineNumber":179,"sourceCode":"    if (parsed.username || parsed.password || parsed.hash) {\n      throw new Error('url must not contain credentials or fragments; use OneCLI for authentication');\n    }\n    for (const key of parsed.searchParams.keys()) {\n      if (SECRET_QUERY_KEY_RE.test(key.replace(CAMEL_SPLIT_RE, '$1_$2'))) {\n        throw new Error(`url query parameter \"${key}\" looks like a credential; use OneCLI for authentication`);\n      }\n    }\n    const headers = parseStringRecord(input.headers, 'headers');\n    return {\n      type: 'http',\n      url,\n      ...(headers === undefined ? {} : { headers }),\n      ...(instructions === undefined ? {} : { instructions }),\n    };\n  }\n  if (command === undefined) throw new Error('Provide exactly one of command or url');\n\n  if (input.headers !== undefined) throw new Error('headers is only valid with url');\n  const args = input.args ?? [];\n  if (!Array.isArray(args) || !args.every((arg) => typeof arg === 'string')) {\n    throw new Error('args must be a JSON array of strings');\n  }\n  const env = parseStringRecord(input.env, 'env') ?? {};\n  for (const key of Object.keys(env)) {\n    if (!ENV_KEY_RE.test(key)) {\n      throw new Error(`env key ${JSON.stringify(key)} must be a valid environment variable name`);\n    }\n  }\n  const cwd = parseCwd(input.cwd);\n  return {\n    command,\n    args,\n    env,\n    ...(cwd === undefined ? {} : { cwd }),\n    ...(instructions === undefined ? {} : { instructions }),\n  };","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/container-config.ts#L161-L197","documentation":"A stdio (command-based) MCP entry includes a `headers` field. Headers only apply to url/http transport; for a local process they are meaningless, so parseMcpServerConfig rejects them (container-config.ts:179).","triggerScenarios":"{\"command\":\"npx\",\"args\":[...],\"headers\":{\"Authorization\":\"...\"}} passed via ncl add-mcp-server or the self-mod tool.","commonSituations":"Converting an http entry to stdio and leaving headers behind; attempting to pass auth to the wrapped process via headers instead of env.","solutions":["Remove headers from the stdio entry","Pass configuration to the process via env instead (non-secret values only)","Store real credentials in OneCLI, never in headers/env config"],"exampleFix":"// before\n{\"command\":\"npx\",\"args\":[\"srv\"],\"headers\":{\"X-Api-Key\":\"k\"}}\n// after\n{\"command\":\"npx\",\"args\":[\"srv\"]}","handlingStrategy":"validation","validationCode":"if (entry.command !== undefined) delete entry.headers;","typeGuard":"const isCleanStdioEntry = (e: any) => e.command !== undefined && e.headers === undefined;","tryCatchPattern":"catch (err) { if (err.message.includes('headers is only valid with url')) stripHeaders(); else throw err; }","preventionTips":["Remember headers belong to url transport only","Use env for process config, OneCLI for secrets"],"tags":["mcp","config-validation","transport-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}