gastownhall/beads · error
metadata.repo must not be null
Error message
metadata.repo must not be null
What it means
After unmarshaling, githubRepoFromIssue checks the decoded `repo` value; if it decodes to JSON null (rather than being absent), the function rejects it with "metadata.repo must not be null". A null repo is indistinguishable from corruption, so it fails loudly instead of silently skipping gate checks.
Source
Thrown at cmd/bd/gate.go:874
if issue == nil || len(issue.Metadata) == 0 || string(issue.Metadata) == "null" {
return "", nil
}
var raw map[string]json.RawMessage
if err := json.Unmarshal(issue.Metadata, &raw); err != nil {
return "", fmt.Errorf("metadata must be a JSON object: %w", err)
}
repoRaw, hasRepo := raw["repo"]
if !hasRepo {
return "", nil
}
var repoValue interface{}
if err := json.Unmarshal(repoRaw, &repoValue); err != nil {
return "", fmt.Errorf("metadata.repo: %w", err)
}
if repoValue == nil {
return "", fmt.Errorf("metadata.repo must not be null")
}
repo, ok := repoValue.(string)
if !ok {
return "", fmt.Errorf("metadata.repo must be a string, got %T", repoValue)
}
if repo == "" {
return "", nil
}
parts := strings.Split(repo, "/")
if len(parts) != 2 && len(parts) != 3 {
return "", fmt.Errorf("repo %q must use OWNER/REPO or HOST/OWNER/REPO", repo)
}
for _, part := range parts {
if part == "" {
return "", fmt.Errorf("repo %q contains an empty path component", repo)
}
for _, char := range part {View on GitHub (pinned to 71377f2769)
Solutions
- Delete the `repo` key entirely rather than setting it to null — absence means "no repo configured" and returns empty string gracefully.
- Set a valid value: `"repo": "owner/repo"` or `"host/owner/repo"` in the issue metadata.
- Update whatever writer (script or tool) nulls the field so it removes the key instead.
Example fix
// before
{"repo": null}
// after
{} // or {"repo": "gastownhall/beads"} Defensive patterns
Strategy: validation
Validate before calling
var v interface{}
if err := json.Unmarshal(md["repo"], &v); err == nil && v == nil {
delete(md, "repo") // treat null as "no repo" instead of leaving null
} Type guard
func repoMetadataIsPresent(md map[string][]byte) bool {
raw, ok := md["repo"]
if !ok { return false }
var v interface{}
if err := json.Unmarshal(raw, &v); err != nil { return false }
return v != nil
} Prevention
- Never set metadata fields to null; delete the key instead.
- Audit scripts that clear issue fields for null writes.
- Add a pre-submit check rejecting `"repo": null` in metadata.
When it happens
Trigger: The issue metadata stores `"repo": null` explicitly. json.Unmarshal succeeds and yields repoValue == nil, hitting this branch during repoMetadataForGate or any GH run/PR gate check.
Common situations: Automation that clears fields by setting them to null instead of deleting the key; partial JSON merges that zeroed the value; hand-edited issue exports.
Related errors
- ExternalDoltConfig: must set Socket or (Host, Port)
- metadata.repo: %w
- loading config for gate resolution: %w
- invalid metadata: %w
- invalid metadata: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b6aa7af6becfdd12.
Report an issue: GitHub.