{"record":{"id":"147837f256207c42","repo":"vercel/ai","slug":"pi-mcp-server-json-stringify-name-must-be-conf","errorCode":null,"errorMessage":"Pi MCP server ${JSON.stringify(name)} must be configured with an object value.","messagePattern":"Pi MCP server (.+?) must be configured with an object value\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-pi/src/pi-session.ts","lineNumber":1566,"sourceCode":"        harnessId: HARNESS_ID,\n        specificationVersion: 'harness-v1',\n        data: sessionFileName ? { sessionFileName } : {},\n      };\n    },\n  };\n\n  return sessionImpl;\n}\n\nfunction resolvePiMcpServers({\n  mcpServers,\n}: {\n  mcpServers: Record<string, unknown> | undefined;\n}): Record<string, unknown> {\n  if (mcpServers == null) return {};\n  for (const [name, value] of Object.entries(mcpServers)) {\n    if (value == null || typeof value !== 'object' || Array.isArray(value)) {\n      throw new Error(\n        `Pi MCP server ${JSON.stringify(name)} must be configured with an object value.`,\n      );\n    }\n  }\n  return mcpServers;\n}\n\n/**\n * Whether a terminal error (string from Pi's event stream, or a thrown error)\n * is an abort — the expected result of `doSuspendTurn` aborting the in-flight\n * turn. Only these are safe to swallow while `suspending`; any other error is\n * unanticipated and must surface as an `error` chunk.\n */\nfunction isAbortError(value: unknown): boolean {\n  if (value == null) return false;\n  if (\n    typeof value === 'object' &&\n    (value as { name?: unknown }).name === 'AbortError'","sourceCodeStart":1548,"sourceCodeEnd":1584,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-pi/src/pi-session.ts#L1548-L1584","documentation":"The Pi harness validates that each entry in the mcpServers configuration map is a plain object. Null, primitive, or array values cannot describe an MCP server, so the library throws this error naming the offending server key. It fails fast at session creation instead of producing a confusing failure later when the MCP server is started.","triggerScenarios":"Passing mcpServers where a value is null/undefined, a string or number, or an array — e.g. `{ mcpServers: { filesystem: null } }` or `{ mcpServers: { fs: ['node','mcp.js'] } }`. Triggered during session setup via the validation function called with the resolved mcpServers record.","commonSituations":"Copy-pasting a command-style array config (as some MCP launchers accept) into the Pi harness which expects an object with command/args/env fields; environment-driven config where an env var is unset and injected as null; YAML/JSON configs where an empty key collapses to null.","solutions":["Change each mcpServers value to a plain object with the required fields (e.g. command, args, env).","Remove null/undefined entries from the map entirely instead of leaving empty placeholders.","Convert array-form server definitions into object form.","Validate the config with a schema (e.g. zod) before constructing the session."],"exampleFix":"// before\ncreatePi({\n  mcpServers: {\n    filesystem: null,\n    fetch: [\"npx\", \"-y\", \"mcp-server-fetch\"],\n  },\n});\n// after\ncreatePi({\n  mcpServers: {\n    filesystem: { command: \"npx\", args: [\"-y\", \"mcp-server-fs\"] },\n    fetch: { command: \"npx\", args: [\"-y\", \"mcp-server-fetch\"] },\n  },\n});","handlingStrategy":"validation","validationCode":"function validateMcpServers(servers) {\n  for (const [name, value] of Object.entries(servers ?? {})) {\n    if (value == null || typeof value !== 'object' || Array.isArray(value)) {\n      throw new Error(`MCP server '${name}' must be an object`);\n    }\n  }\n}\nvalidateMcpServers(config.mcpServers);","typeGuard":"function isMcpServerConfig(v: unknown): v is Record<string, unknown> {\n  return v != null && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  await createPi({ mcpServers: config.mcpServers });\n} catch (err) {\n  if (String(err.message).includes('must be configured with an object value')) {\n    const name = err.message.match(/Pi MCP server \"([^\"]+)\"/)?.[1];\n    throw new Error(`Fix mcpServers.${name}: must be an object with command/args`, { cause: err });\n  }\n  throw err;\n}","preventionTips":["Never leave null placeholders in mcpServers; delete unused keys","Keep all server values in object form (command/args/env)","Validate the full config with a schema before session creation"],"tags":["configuration","mcp","validation"],"backgroundTag":"invalid-config-value","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}