BloopAI/vibe-kanban · error

Servers configuration must be an object

Error message

Servers configuration must be an object

What it means

After walking the full servers_path in validateFullConfig, the final value must be a JSON object of server entries. If it is an array, string, number, boolean, or null, this error is thrown. The servers section exists but has the wrong type.

Source

Thrown at packages/web-core/src/shared/lib/mcpStrategies.ts:49

    mcp_config: McpConfig,
    full_config: JsonValue
  ): void {
    let current: JsonValue = full_config;
    for (const key of mcp_config.servers_path) {
      if (!isJsonObject(current)) {
        throw new Error(
          `Expected object at path: ${mcp_config.servers_path.join('.')}`
        );
      }
      current = current[key];
      if (current === undefined) {
        throw new Error(
          `Missing required field at path: ${mcp_config.servers_path.join('.')}`
        );
      }
    }
    if (!isJsonObject(current)) {
      throw new Error('Servers configuration must be an object');
    }
  }
  static extractServersForApi(
    mcp_config: McpConfig,
    full_config: JsonValue
  ): JsonObject {
    let current: JsonValue = full_config;
    for (const key of mcp_config.servers_path) {
      if (!isJsonObject(current)) {
        throw new Error(
          `Expected object at path: ${mcp_config.servers_path.join('.')}`
        );
      }
      current = current[key];
      if (current === undefined) {
        throw new Error(
          `Missing required field at path: ${mcp_config.servers_path.join('.')}`
        );

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Reshape the value at servers_path to an object keyed by server name: { "server-name": { command, args, ... } }.
  2. Regenerate the section with McpConfigStrategyGeneral.createFullConfig(cfg) using cfg.servers.
  3. Diff the file against a known-good config for this MCP client.
  4. Back up the config before manual edits so you can restore the object shape.

Example fix

// before
{ "mcp": { "servers": ["fetch", "fs"] } } // array -> throws
// after
{ "mcp": { "servers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] } } } }
Defensive patterns

Strategy: validation

Validate before calling

function serversSectionIsObject(full, path) {
  let cur = full;
  for (const k of path) {
    if (typeof cur !== 'object' || cur === null || Array.isArray(cur)) return false;
    cur = cur[k];
  }
  return typeof cur === 'object' && cur !== null && !Array.isArray(cur);
}
if (!serversSectionIsObject(fullConfig, cfg.servers_path)) console.warn('Servers section must be an object');

Type guard

function isServersObject(v: unknown): v is Record<string, Record<string, unknown>> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

Try / catch

try {
  McpConfigStrategyGeneral.validateFullConfig(cfg, fullConfig);
} catch (e) {
  if (e instanceof Error && e.message === 'Servers configuration must be an object') {
    fullConfig = McpConfigStrategyGeneral.createFullConfig(cfg); // rewrite from cfg.servers
  } else throw e;
}

Prevention

When it happens

Trigger: servers_path resolves to a non-object — e.g. mcp.servers is an array of names instead of an object keyed by server name, or was overwritten by a scalar during a bad edit/merge.

Common situations: Hand-editing the config and typing servers as a list; a migration script converting the object to an array; pasting a config snippet from docs that uses a different shape.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/2269065ef77e6dda. Report an issue: GitHub.