gastownhall/beads · error
existing milestone metadata is not a JSON object: %w
Error message
existing milestone metadata is not a JSON object: %w
What it means
mergedLinearMilestoneMetadata merges existing issue metadata JSON with fresh Linear milestone data before syncing to Linear. It requires the stored metadata to be a JSON object (unmarshalable into map[string]interface{}); if the existing raw metadata is any other JSON value (array, string, number), json.Unmarshal into the map fails and this error wraps the underlying parse error.
Source
Thrown at cmd/bd/linear.go:685
}
ref := ""
if issue.ExternalRef != nil {
ref = strings.TrimSpace(*issue.ExternalRef)
}
if ref == "" {
return issue, nil
}
}
return nil, nil
}
func mergedLinearMilestoneMetadata(existing json.RawMessage, ms *linear.ProjectMilestone) (json.RawMessage, error) {
data := make(map[string]interface{})
if len(existing) > 0 {
trimmed := strings.TrimSpace(string(existing))
if trimmed != "" && trimmed != "null" {
if err := json.Unmarshal(existing, &data); err != nil {
return nil, fmt.Errorf("existing milestone metadata is not a JSON object: %w", err)
}
}
}
linearMeta, _ := data["linear"].(map[string]interface{})
if linearMeta == nil {
linearMeta = make(map[string]interface{})
}
linearMeta["kind"] = "project_milestone"
linearMeta["project_milestone"] = map[string]interface{}{
"id": strings.TrimSpace(ms.ID),
"name": ms.Name,
"description": ms.Description,
"progress": ms.Progress,
"targetDate": ms.TargetDate,
}
data["linear"] = linearMeta
View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the issue metadata (bd show or query the metadata column) and fix it to be a JSON object, e.g. `{}` or `{"linear":{...}}`
- If the metadata is garbage, reset it to an empty object or delete the issue's metadata so merging starts fresh
- Re-run bd linear pull/sync after correcting the metadata
Example fix
// before (metadata column)
["legacy","format"]
// after
{"linear":{"milestoneId":"..."}} Defensive patterns
Strategy: validation
Validate before calling
func isJSONObject(raw json.RawMessage) bool {
t := strings.TrimSpace(string(raw))
if t == "" || t == "null" {
return true
}
var m map[string]interface{}
return json.Unmarshal(raw, &m) == nil
} Type guard
func asMetadataObject(raw json.RawMessage) (map[string]interface{}, bool) {
var m map[string]interface{}
if err := json.Unmarshal(raw, &m); err != nil || m == nil {
return nil, false
}
return m, true
} Try / catch
merged, err := mergedLinearMilestoneMetadata(existing, ms)
if err != nil {
var syncErr = fmt.Errorf("milestone sync skipped for %s: %w", issue.ID, err)
log.Warn(syncErr)
// reset metadata to {} and retry once
} Prevention
- Only write JSON objects into issue metadata; never arrays or bare strings
- Add a schema check in any tool that writes bd metadata
- Spot-check metadata after imports/migrations
When it happens
Trigger: Calling ensureLinearMilestoneEpic when the issue's metadata column contains a non-object JSON value (e.g. `[1,2]`, `"text"`, or `42`) rather than an object like `{"linear":{...}}`.
Common situations: Metadata was hand-edited or written by an older/buggy bd version or external tool that stored a JSON array/string instead of an object; a migration or import wrote malformed metadata into the issue record.
Related errors
- marshaling Linear milestone metadata: %w
- ExternalDoltConfig: must set Socket or (Host, Port)
- metadata must be a JSON object: %w
- metadata.repo: %w
- metadata.repo must not be null
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8a6f9c587659d9bf.
Report an issue: GitHub.