{"record":{"id":"6872d2ce252ff380","repo":"nanocoai/nanoclaw","slug":"env-key-json-stringify-key-must-be-a-valid-env","errorCode":null,"errorMessage":"env key ${JSON.stringify(key)} must be a valid environment variable name","messagePattern":"env key (.+?) must be a valid environment variable name","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/container-config.ts","lineNumber":187,"sourceCode":"    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  };\n}\n\nfunction parseStringRecord(value: unknown, flag: string): Record<string, string> | undefined {\n  if (value === undefined) return undefined;\n  if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n    throw new Error(`${flag} must be a JSON object with string values`);\n  }\n  const record: Record<string, string> = {};","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/container-config.ts#L169-L205","documentation":"An env key in a stdio MCP entry fails the ENV_KEY_RE pattern for environment variable names (typically /^[A-Za-z_][A-Za-z0-9_]*$/). Keys with spaces, dashes, leading digits, or empty strings are rejected before they could break the container's env handling.","triggerScenarios":"env entries like {\"MY-VAR\":\"x\"}, {\"1VAR\":\"x\"}, {\"my var\":\"x\"}, or {\"\":\"x\"} in a stdio MCP server config.","commonSituations":"Copying header names (often dash-separated) into env; lowercase-with-dashes config style; templating artifacts producing empty keys.","solutions":["Rename keys to uppercase with underscores: MY-VAR → MY_VAR","Ensure keys start with a letter or underscore","Remove empty-string keys from the env object"],"exampleFix":"// before\n{\"env\":{\"my-var\":\"value\"}}\n// after\n{\"env\":{\"MY_VAR\":\"value\"}}","handlingStrategy":"validation","validationCode":"for (const k of Object.keys(entry.env ?? {})) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new UserError(`bad env key: ${k}`);","typeGuard":"const hasValidEnvKeys = (e: any) => Object.keys(e.env ?? {}).every(k => /^[A-Za-z_][A-Za-z0-9_]*$/.test(k));","tryCatchPattern":"catch (err) { if (err.message.includes('valid environment variable name')) normalizeEnvKeys(); else throw err; }","preventionTips":["Uppercase + underscores for env keys","Don't copy dash-style header names into env"],"tags":["mcp","config-validation","environment-variables"],"backgroundTag":"invalid-env-var-name","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}