gastownhall/beads · error
Linear project milestone is missing id
Error message
Linear project milestone is missing id
What it means
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.
Source
Thrown at cmd/bd/linear.go:579
}
return hooks
}
const linearMilestoneExternalRefPrefix = "linear:project-milestone:"
func linearMilestoneExternalRef(id string) string {
return linearMilestoneExternalRefPrefix + strings.TrimSpace(id)
}
func isLinearMilestoneExternalRef(ref string) bool {
return strings.HasPrefix(strings.TrimSpace(ref), linearMilestoneExternalRefPrefix)
}
func ensureLinearMilestoneEpic(ctx context.Context, st storage.Storage, ms *linear.ProjectMilestone, actor string, generateID func(context.Context, *types.Issue) error) (string, error) {
milestoneID := strings.TrimSpace(ms.ID)
if milestoneID == "" {
return "", fmt.Errorf("Linear project milestone is missing id")
}
title := strings.TrimSpace(ms.Name)
if title == "" {
title = milestoneID
}
description := ms.Description
ref := linearMilestoneExternalRef(milestoneID)
metadata, err := mergedLinearMilestoneMetadata(nil, ms)
if err != nil {
return "", err
}
existing, err := findLinearMilestoneEpic(ctx, st, ref, milestoneID, title)
if err != nil {
return "", err
}
if existing != nil {View on GitHub (pinned to 71377f2769)
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
Example fix
// before
ensureLinearMilestoneEpic(ctx, st, &linear.ProjectMilestone{Name: "v1"}, ...)
// after
if strings.TrimSpace(ms.ID) == "" { return } // skip; then
ensureLinearMilestoneEpic(ctx, st, ms, ...) Defensive patterns
Strategy: validation
Validate before calling
// guard before calling the sync conversion
if ms == nil || strings.TrimSpace(ms.ID) == "" {
return fmt.Errorf("skipping milestone with empty ID")
} Type guard
func validMilestone(ms *linear.ProjectMilestone) bool {
return ms != nil && strings.TrimSpace(ms.ID) != ""
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Linear API returning partial data (deprecated endpoint, scope changes); custom import scripts constructing milestone objects manually; upstream Linear schema changes after a library update.
Related errors
- invalid pull filter: %w
- missing external ref for Linear issue %s
- generating Linear milestone epic ID: %w
- creating Linear milestone epic %q: %w
- searching local issues for Linear milestone %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/627dd0220585c97e.
Report an issue: GitHub.