gastownhall/beads · error

missing external ref for Linear issue %s

Error message

missing external ref for Linear issue %s

What it means

During Linear import/sync, when converting dependencies involving a project milestone, the code requires the milestone's epic external ref. If the resolved ref string is empty, this error aborts conversion for that issue, since a dependency cannot be recorded without a FromExternalID.

Source

Thrown at cmd/bd/linear.go:551

		}
		hooks.GenerateID = generateID
	}

	if opts.Milestones && st != nil {
		hooks.AfterConvert = func(ctx context.Context, extIssue *tracker.TrackerIssue, conv *tracker.IssueConversion, ref string, _ *types.Issue, syncOpts tracker.SyncOptions) error {
			li, ok := extIssue.Raw.(*linear.Issue)
			if !ok || li == nil || li.ProjectMilestone == nil {
				return nil
			}
			if syncOpts.DryRun || opts.DryRun {
				return nil
			}
			milestoneRef, err := ensureLinearMilestoneEpic(ctx, st, li.ProjectMilestone, hookActor, generateID)
			if err != nil {
				return err
			}
			if strings.TrimSpace(ref) == "" {
				return fmt.Errorf("missing external ref for Linear issue %s", extIssue.Identifier)
			}
			conv.Dependencies = append(conv.Dependencies, tracker.DependencyInfo{
				FromExternalID: ref,
				ToExternalID:   milestoneRef,
				Type:           string(types.DepParentChild),
				Source:         tracker.DependencySourceParent,
			})
			return nil
		}
	}

	return hooks
}

const linearMilestoneExternalRefPrefix = "linear:project-milestone:"

func linearMilestoneExternalRef(id string) string {
	return linearMilestoneExternalRefPrefix + strings.TrimSpace(id)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the Linear issue's project milestone data for empty/blank identifiers
  2. Re-run the sync so ensureLinearMilestoneEpic recreates the milestone epic with a valid external ref
  3. Check for prior import failures that left the milestone epic without an external ref and repair or delete that epic record
Defensive patterns

Strategy: validation

Validate before calling

// before appending the dependency, verify the ref
if strings.TrimSpace(ref) == "" {
    log.Printf("skipping dependency for %s: empty external ref", extIssue.Identifier)
    return nil
}

Type guard

func hasExternalRef(issue *types.Issue) bool {
    return issue.ExternalRef != nil && strings.TrimSpace(*issue.ExternalRef) != ""
}

Try / catch

// the sync pipeline returns this error directly; catch at the import boundary
if err := runLinearSync(ctx); err != nil {
    if strings.Contains(err.Error(), "missing external ref") { /* log issue ID and continue/skip */ }
    return err
}

Prevention

When it happens

Trigger: Linear sync hits an issue whose milestone epic lookup/creation returned an empty ref string (e.g. ensureLinearMilestoneEpic returned empty without error due to unexpected data) while appending a parent-child dependency to conv.Dependencies.

Common situations: Linear workspace data quirks — milestone objects with empty identifiers; a partially completed import where the milestone epic was matched but ref assignment was skipped; custom sync scripts feeding unusual Linear payloads.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/ee6641275f4de45c. Report an issue: GitHub.