{"record":{"id":"627dd0220585c97e","repo":"gastownhall/beads","slug":"linear-project-milestone-is-missing-id","errorCode":null,"errorMessage":"Linear project milestone is missing id","messagePattern":"Linear project milestone is missing id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/linear.go","lineNumber":579,"sourceCode":"\t}\n\n\treturn hooks\n}\n\nconst linearMilestoneExternalRefPrefix = \"linear:project-milestone:\"\n\nfunc linearMilestoneExternalRef(id string) string {\n\treturn linearMilestoneExternalRefPrefix + strings.TrimSpace(id)\n}\n\nfunc isLinearMilestoneExternalRef(ref string) bool {\n\treturn strings.HasPrefix(strings.TrimSpace(ref), linearMilestoneExternalRefPrefix)\n}\n\nfunc ensureLinearMilestoneEpic(ctx context.Context, st storage.Storage, ms *linear.ProjectMilestone, actor string, generateID func(context.Context, *types.Issue) error) (string, error) {\n\tmilestoneID := strings.TrimSpace(ms.ID)\n\tif milestoneID == \"\" {\n\t\treturn \"\", fmt.Errorf(\"Linear project milestone is missing id\")\n\t}\n\ttitle := strings.TrimSpace(ms.Name)\n\tif title == \"\" {\n\t\ttitle = milestoneID\n\t}\n\tdescription := ms.Description\n\tref := linearMilestoneExternalRef(milestoneID)\n\n\tmetadata, err := mergedLinearMilestoneMetadata(nil, ms)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\texisting, err := findLinearMilestoneEpic(ctx, st, ref, milestoneID, title)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif existing != nil {","sourceCodeStart":561,"sourceCodeEnd":597,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/linear.go#L561-L597","documentation":"ensureLinearMilestoneEpic validates that the incoming linear.ProjectMilestone has a non-empty ID before creating or updating its epic representation in beads. A milestone without an ID cannot be referenced or deduplicated, so the function fails fast with this error.","triggerScenarios":"Linear sync encounters a ProjectMilestone object whose ID field is empty or whitespace-only — typically from a malformed API response or hand-crafted webhook payload passed into the conversion pipeline.","commonSituations":"Linear API returning partial data (deprecated endpoint, scope changes); custom import scripts constructing milestone objects manually; upstream Linear schema changes after a library update.","solutions":["Verify the Linear API response actually includes milestone IDs (check API scopes/token and endpoint version)","Skip or log-and-continue for milestones lacking IDs in your sync script before calling the conversion path","Refresh/re-fetch milestone data from Linear and re-run the import"],"exampleFix":"// before\nensureLinearMilestoneEpic(ctx, st, &linear.ProjectMilestone{Name: \"v1\"}, ...)\n// after\nif strings.TrimSpace(ms.ID) == \"\" { return } // skip; then\nensureLinearMilestoneEpic(ctx, st, ms, ...)","handlingStrategy":"validation","validationCode":"// guard before calling the sync conversion\nif ms == nil || strings.TrimSpace(ms.ID) == \"\" {\n    return fmt.Errorf(\"skipping milestone with empty ID\")\n}","typeGuard":"func validMilestone(ms *linear.ProjectMilestone) bool {\n    return ms != nil && strings.TrimSpace(ms.ID) != \"\"\n}","tryCatchPattern":null,"preventionTips":["Skip-and-log milestones with empty IDs in custom sync scripts","Check Linear API token scopes/endpoints when IDs come back empty","Pin and test the Linear SDK version against your workspace's API responses"],"tags":["linear","validation","missing-id","sync"],"backgroundTag":"missing-required-id","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}