{"record":{"id":"cd80103c82ad9ad3","repo":"gastownhall/beads","slug":"metadata-must-be-a-json-object-w","errorCode":null,"errorMessage":"metadata must be a JSON object: %w","messagePattern":"metadata must be a JSON object: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/gate.go","lineNumber":862,"sourceCode":"\t}\n\treturn true\n}\n\n// githubRepoFromIssue returns a validated [HOST/]OWNER/REPO value from metadata.repo.\n// A missing repo key, or metadata without a repo key at all, means the current\n// Git repository should be used. An explicit `\"repo\":null` or a non-string\n// repo value is rejected as malformed rather than silently falling back to\n// the current repository - the docs promise malformed values are rejected,\n// and a silent fallback here is the dangerous direction (it can point a\n// cross-repo check at the wrong repository instead of failing loudly).\nfunc githubRepoFromIssue(issue *types.Issue) (string, error) {\n\tif issue == nil || len(issue.Metadata) == 0 || string(issue.Metadata) == \"null\" {\n\t\treturn \"\", nil\n\t}\n\n\tvar raw map[string]json.RawMessage\n\tif err := json.Unmarshal(issue.Metadata, &raw); err != nil {\n\t\treturn \"\", fmt.Errorf(\"metadata must be a JSON object: %w\", err)\n\t}\n\trepoRaw, hasRepo := raw[\"repo\"]\n\tif !hasRepo {\n\t\treturn \"\", nil\n\t}\n\n\tvar repoValue interface{}\n\tif err := json.Unmarshal(repoRaw, &repoValue); err != nil {\n\t\treturn \"\", fmt.Errorf(\"metadata.repo: %w\", err)\n\t}\n\tif repoValue == nil {\n\t\treturn \"\", fmt.Errorf(\"metadata.repo must not be null\")\n\t}\n\trepo, ok := repoValue.(string)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"metadata.repo must be a string, got %T\", repoValue)\n\t}\n\tif repo == \"\" {","sourceCodeStart":844,"sourceCodeEnd":880,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/gate.go#L844-L880","documentation":"githubRepoFromIssue in cmd/bd/gate.go extracts a GitHub repo from an issue's Metadata field, which must be a JSON object. If issue.Metadata is non-empty but fails to unmarshal into map[string]json.RawMessage, the error is wrapped as 'metadata must be a JSON object: %w'. Empty or null metadata returns (\"\", nil) silently, so this error only fires on present-but-malformed metadata.","triggerScenarios":"Calling gate checks (checkGHRun, checkGHPR, matchGatesToRuns via repoMetadataForGate) on an issue whose Metadata is present but not a JSON object — e.g. a JSON array, string, number, or corrupted bytes.","commonSituations":"Metadata written by older bd versions or external tools in a different shape; manual edits to issue metadata corrupting the JSON; metadata stored as a quoted JSON string (double-encoded) rather than an object; partial writes leaving truncated JSON.","solutions":["Inspect the issue metadata (bd show <id> / export) and fix it to be a valid JSON object containing a \"repo\" key.","If metadata was double-encoded (a JSON string containing JSON), decode it once so the field holds an actual object.","Restore correct metadata via bd update or by re-running the workflow that sets repo metadata.","Check the wrapped cause for the exact JSON syntax error if the metadata appears valid at a glance."],"exampleFix":"// before\n\"metadata\": \"{\\\"repo\\\": \\\"owner/name\\\"}\"   // double-encoded string\n// after\n\"metadata\": {\"repo\": \"owner/name\"}         // actual JSON object","handlingStrategy":"type-guard","validationCode":"// before relying on gate GitHub metadata\nvar m map[string]json.RawMessage\nif err := json.Unmarshal(issue.Metadata, &m); err != nil || m[\"repo\"] == nil {\n    return errors.New(\"issue metadata is not an object with a repo key\")\n}","typeGuard":"// Go\nfunc hasRepoMetadata(md json.RawMessage) bool {\n    var m map[string]json.RawMessage\n    return json.Unmarshal(md, &m) == nil && m[\"repo\"] != nil\n}","tryCatchPattern":"if repo, err := githubRepoFromIssue(issue); err != nil {\n    if strings.Contains(err.Error(), \"metadata must be a JSON object\") {\n        // repair or clear the issue metadata before retrying the gate\n    }\n}","preventionTips":["Store issue metadata as a JSON object, never a double-encoded string.","Validate metadata JSON after manual edits or migrations.","Use bd update commands rather than raw DB edits to change metadata.","Pin consistent bd versions across tools that write issue metadata."],"tags":["gate","github","metadata","json"],"backgroundTag":"metadata-not-json-object","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}