{"record":{"id":"47ec6549df892f13","repo":"multica-ai/multica","slug":"entry-must-be-a-json-object","errorCode":null,"errorMessage":"entry must be a JSON object","messagePattern":"entry must be a JSON object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/pkg/agent/opencode_mcp.go","lineNumber":203,"sourceCode":"\n// validateOpenCodeNativeMCPEntry strict-decodes one native-shape entry\n// against OpenCode's schema and returns the equivalent map[string]any\n// representation. The decode is intentionally strict\n// (DisallowUnknownFields) — any field outside the McpLocalConfig /\n// McpRemoteConfig / `{enabled: bool}` shapes is rejected, matching the\n// schema's `additionalProperties: false` and surfacing user typos as\n// errors before they reach OpenCode.\nfunc validateOpenCodeNativeMCPEntry(name string, raw json.RawMessage) (map[string]any, error) {\n\twrap := func(err error) error {\n\t\treturn fmt.Errorf(\"opencode mcp_config: server %q: %w\", name, err)\n\t}\n\n\t// JSON-object guard: the discriminator probe and strict decoders\n\t// below assume an object; without this guard a primitive (string,\n\t// number, array, null) would surface a confusing decoder error.\n\ttrimmed := bytes.TrimSpace(raw)\n\tif len(trimmed) == 0 || trimmed[0] != '{' {\n\t\treturn nil, wrap(errors.New(\"entry must be a JSON object\"))\n\t}\n\n\t// Discriminator probe: peek `type` to choose the right strict\n\t// decode target. This first decode is intentionally permissive so\n\t// \"type: 5\" surfaces a clear \"type must be a string\" error rather\n\t// than the strict-decode generic \"json: cannot unmarshal number\".\n\tvar probe struct {\n\t\tType *json.RawMessage `json:\"type,omitempty\"`\n\t}\n\tif err := json.Unmarshal(raw, &probe); err != nil {\n\t\treturn nil, wrap(fmt.Errorf(\"parse: %w\", err))\n\t}\n\tvar typeStr string\n\tif probe.Type != nil {\n\t\tif err := json.Unmarshal(*probe.Type, &typeStr); err != nil {\n\t\t\treturn nil, wrap(fmt.Errorf(\"`type` must be a string, got %s\", strings.TrimSpace(string(*probe.Type))))\n\t\t}\n\t}","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/pkg/agent/opencode_mcp.go#L185-L221","documentation":"Thrown by validateOpenCodeNativeMCPEntry when a per-server entry in the agent's opencode mcp_config is not a JSON object. The validator's discriminator probe and strict decoders assume an object, so a primitive (string, number, array, null, or empty raw) is rejected up front with this clear message instead of a confusing decoder error. The message is wrapped as `opencode mcp_config: server \"<name>\": ...`.","triggerScenarios":"Setting mcp_config.<name> to a scalar or array, e.g. \"mcp_config\": {\"github\": \"https://mcp.github.com\"} or {\"github\": [\"npx\", \"-y\", \"server\"]} or {\"github\": null}, via the agent config API or config file.","commonSituations":"Copying an SSH-style or Claude-style config line (bare command string / argv array) into OpenCode's native mcp_config; YAML/JSON config authoring mistakes; passing a URL string where a {\"type\":\"remote\",\"url\":...} object is required.","solutions":["Wrap each server entry in a JSON object with an explicit type: {\"type\":\"remote\",\"url\":\"...\"} or {\"type\":\"local\",\"command\":[...]}","Remove null entries for servers you do not want to configure.","Re-check the OpenCode mcp_config schema for the supported variants (local, remote, bare {\"enabled\": bool})."],"exampleFix":"// before\n\"mcp_config\": { \"github\": \"https://mcp.github.com/sse\" }\n\n// after\n\"mcp_config\": { \"github\": { \"type\": \"remote\", \"url\": \"https://mcp.github.com/sse\" } }","handlingStrategy":"type-guard","validationCode":"// Validate before submitting mcp_config\nfor name, raw := range mcpConfig {\n    if !isObject(raw) {\n        return fmt.Errorf(\"mcp_config entry %q must be a JSON object\", name)\n    }\n}","typeGuard":"function isMCPEntryObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["Always author mcp_config entries as objects with an explicit type discriminator.","Validate the whole mcp_config with the same schema the server uses before saving it in agent settings.","Never inline command strings or argv arrays directly as the entry value."],"tags":["json","config","mcp","validation","opencode"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}