{"record":{"id":"60fe45f2afedac0c","repo":"BloopAI/vibe-kanban","slug":"expected-object-at-path-mcp-config-servers-path","errorCode":null,"errorMessage":"Expected object at path: ${mcp_config.servers_path.join('.')}","messagePattern":"Expected object at path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/web-core/src/shared/lib/mcpStrategies.ts","lineNumber":37,"sourceCode":"        : undefined;\n      if (!next) current[key] = {};\n      current = current[key] as JsonObject;\n    }\n\n    if (cfg.servers_path.length > 0) {\n      const lastKey = cfg.servers_path[cfg.servers_path.length - 1];\n      current[lastKey] = cfg.servers;\n    }\n    return fullConfig;\n  }\n  static validateFullConfig(\n    mcp_config: McpConfig,\n    full_config: JsonValue\n  ): void {\n    let current: JsonValue = full_config;\n    for (const key of mcp_config.servers_path) {\n      if (!isJsonObject(current)) {\n        throw new Error(\n          `Expected object at path: ${mcp_config.servers_path.join('.')}`\n        );\n      }\n      current = current[key];\n      if (current === undefined) {\n        throw new Error(\n          `Missing required field at path: ${mcp_config.servers_path.join('.')}`\n        );\n      }\n    }\n    if (!isJsonObject(current)) {\n      throw new Error('Servers configuration must be an object');\n    }\n  }\n  static extractServersForApi(\n    mcp_config: McpConfig,\n    full_config: JsonValue\n  ): JsonObject {","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/packages/web-core/src/shared/lib/mcpStrategies.ts#L19-L55","documentation":"validateFullConfig walks mcp_config.servers_path key-by-key through the full (raw app) config JSON. Before reading each key it asserts the current value is a JSON object; if a mid-path value is an array, string, number, or null it throws this error. It guards against traversing into a non-object when locating the MCP servers section.","triggerScenarios":"servers_path contains a key whose current value is not an object — e.g. path ['mcp','servers'] but full_config.mcp is a string/number/boolean/array/null; a user hand-edited the config file so a path segment holds a scalar.","commonSituations":"A config for one MCP strategy (e.g. a 'mcpServers' object path) applied to a config file shaped for another tool; manual edits corrupting the file; a template that defines a segment as an array.","solutions":["Log/print full_config and compare each segment of servers_path against its actual shape.","Fix the config so every segment except the last is a JSON object, or correct servers_path to match the real structure.","If migrating from another tool's config format, use the matching McpConfig strategy rather than reusing servers_path.","Validate the config JSON before calling handleMcpServersChange/handleApplyMcpServers."],"exampleFix":"// before: path points into a scalar\nvalidateFullConfig(cfg, { mcp: 'not-an-object' }); // throws\n// after: ensure the container is an object first\nconst full = { mcp: { servers: {} } };\nvalidateFullConfig(cfg, full);","handlingStrategy":"validation","validationCode":"function pathIsTraversable(full, path) {\n  let cur = full;\n  for (const k of path) {\n    if (typeof cur !== 'object' || cur === null || Array.isArray(cur)) return false;\n    cur = cur[k];\n  }\n  return true;\n}\nif (!pathIsTraversable(fullConfig, cfg.servers_path)) console.warn('Config not traversable at', cfg.servers_path.join('.'));","typeGuard":"function isJsonObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  McpConfigStrategyGeneral.validateFullConfig(cfg, fullConfig);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Expected object at path')) {\n    fullConfig = McpConfigStrategyGeneral.createFullConfig(cfg); // rebuild\n  } else throw e;\n}","preventionTips":["Validate config JSON shape before passing it into strategy handlers","Keep servers_path in sync with the target MCP client's documented schema","Schema-check hand-edited config files before loading them","Don't share one servers_path across different MCP strategies"],"tags":["config","validation","mcp"],"backgroundTag":"config-path-mismatch","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}