{"record":{"id":"0168b5fddbce73b9","repo":"multica-ai/multica","slug":"body-must-be-a-json-object-or-array","errorCode":null,"errorMessage":"body must be a JSON object or array","messagePattern":"body must be a JSON object or array","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"server/internal/handler/autopilot_webhook.go","lineNumber":131,"sourceCode":"func normalizeWebhookPayload(body []byte, headers http.Header) (WebhookEnvelope, error) {\n\tbody = stripBOM(body)\n\tif len(body) == 0 {\n\t\treturn WebhookEnvelope{}, errors.New(\"empty body\")\n\t}\n\n\t// First, validate JSON shape (object or array). Reject scalars early —\n\t// `\"hello\"` is technically valid JSON but has no useful interpretation\n\t// as a webhook payload and would land in the agent prompt as a bare\n\t// string.\n\tvar asAny any\n\tif err := json.Unmarshal(body, &asAny); err != nil {\n\t\treturn WebhookEnvelope{}, fmt.Errorf(\"invalid json: %w\", err)\n\t}\n\tswitch asAny.(type) {\n\tcase map[string]any, []any:\n\t\t// ok\n\tdefault:\n\t\treturn WebhookEnvelope{}, errors.New(\"body must be a JSON object or array\")\n\t}\n\n\tnow := time.Now().UTC().Format(time.RFC3339)\n\tcontentType := headers.Get(\"Content-Type\")\n\tif i := strings.Index(contentType, \";\"); i >= 0 {\n\t\tcontentType = strings.TrimSpace(contentType[:i])\n\t}\n\n\tenv := WebhookEnvelope{\n\t\tRequest: WebhookRequest{\n\t\t\tReceivedAt:  now,\n\t\t\tContentType: contentType,\n\t\t},\n\t}\n\n\t// 1. Caller-provided envelope.\n\tif obj, ok := asAny.(map[string]any); ok {\n\t\tif eventStr, ok := obj[\"event\"].(string); ok && eventStr != \"\" {","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/handler/autopilot_webhook.go#L113-L149","documentation":"Returned by normalizeWebhookPayload when the body is valid JSON but not an object or array — e.g. a bare string, number, boolean, or null. Per the source comment, a scalar like \"hello\" parses fine but has no useful interpretation as a webhook payload and would land in the agent prompt as a bare primitive, so it is rejected after the early shape check (invalid JSON is a separate error).","triggerScenarios":"POSTing a JSON scalar: body of \"42\", \"true\", \"null\", or a double-quoted string; webhook senders emitting a bare token; clients double-encoding so the top level becomes a string.","commonSituations":"Hand-rolled integrations posting a plain status string; test payloads written as JSON strings; encoding bugs where JSON.stringify is applied twice (the body becomes '\"{\\\"event\\\":...}\"').","solutions":["Wrap the payload in an object: send {\"value\": 42} or {\"message\": \"hello\"} instead of a bare scalar.","Check for double serialization in the sender — the top-level value must be {...} or [...].","Validate the payload shape client-side before posting if you control the sender."],"exampleFix":"# before\ncurl -X POST https://host/api/webhooks/autopilot -d '\"deploy finished\"'\n\n# after\ncurl -X POST https://host/api/webhooks/autopilot \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"event\":\"deploy\",\"message\":\"deploy finished\"}'","handlingStrategy":"type-guard","validationCode":"// Sender side, before POST\nfunction isEnvelopeShape(v: unknown): boolean {\n  return typeof v === \"object\" && v !== null;\n}\nif (!isEnvelopeShape(payload)) {\n  throw new Error(\"webhook payload must be a JSON object or array\");\n}","typeGuard":"function isWebhookEnvelopeShape(v: unknown): v is Record<string, unknown> | unknown[] {\n  return typeof v === \"object\" && v !== null;\n}","tryCatchPattern":null,"preventionTips":["Always post an object or array as the top-level JSON value.","Beware double JSON.stringify — it turns the envelope into a string.","Add a sender-side shape check in shared webhook client code."],"tags":["webhook","validation","json","http","go"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}