{"record":{"id":"b43552114e235ee6","repo":"gastownhall/beads","slug":"updating-linear-milestone-epic-s-w","errorCode":null,"errorMessage":"updating Linear milestone epic %s: %w","messagePattern":"updating Linear milestone epic (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/linear.go","lineNumber":620,"sourceCode":"\t\tif existing.Description != description {\n\t\t\tupdates[\"description\"] = description\n\t\t}\n\t\tif existing.IssueType != types.TypeEpic {\n\t\t\tupdates[\"issue_type\"] = string(types.TypeEpic)\n\t\t}\n\t\tif existing.ExternalRef == nil || strings.TrimSpace(*existing.ExternalRef) != ref {\n\t\t\tupdates[\"external_ref\"] = ref\n\t\t}\n\t\tmergedMetadata, err := mergedLinearMilestoneMetadata(existing.Metadata, ms)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif string(existing.Metadata) != string(mergedMetadata) {\n\t\t\tupdates[\"metadata\"] = mergedMetadata\n\t\t}\n\t\tif len(updates) > 0 {\n\t\t\tif err := st.UpdateIssue(ctx, existing.ID, updates, actor); err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"updating Linear milestone epic %s: %w\", existing.ID, err)\n\t\t\t}\n\t\t}\n\t\treturn ref, nil\n\t}\n\n\texternalRef := ref\n\tepic := &types.Issue{\n\t\tTitle:       title,\n\t\tDescription: description,\n\t\tStatus:      types.StatusOpen,\n\t\tPriority:    2,\n\t\tIssueType:   types.TypeEpic,\n\t\tExternalRef: &externalRef,\n\t\tMetadata:    metadata,\n\t}\n\tif generateID != nil {\n\t\tif err := generateID(ctx, epic); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"generating Linear milestone epic ID: %w\", err)","sourceCodeStart":602,"sourceCodeEnd":638,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/linear.go#L602-L638","documentation":"Wraps st.UpdateIssue failures when syncing an already-existing Linear milestone epic in beads. If merged metadata or other fields differ, the epic is updated; a storage error there is wrapped with the epic's ID via %w.","triggerScenarios":"Linear sync detects changes to a milestone (name/description/metadata) and calls st.UpdateIssue on the existing epic; storage returns an error — concurrent write conflict, locked DB, or the epic was deleted mid-sync.","commonSituations":"Two sync processes running concurrently on the same workspace; Dolt database unavailable; epic issue deleted by a user while a sync was in flight.","solutions":["Check the wrapped storage error and ensure the database is reachable and not locked","Avoid running multiple Linear syncs concurrently against the same workspace","Re-run the sync; the update is a diff-based metadata merge and safe to retry"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-check the epic still exists before updating\nif _, err := st.GetIssue(ctx, existing.ID); err != nil {\n    return \"\", fmt.Errorf(\"milestone epic %s vanished: %w\", existing.ID, err)\n}","typeGuard":null,"tryCatchPattern":"err := st.UpdateIssue(ctx, existing.ID, updates, actor)\nif err != nil {\n    if isTransient(err) { time.Sleep(backoff); return retryUpdate() }\n    return \"\", fmt.Errorf(\"updating Linear milestone epic %s: %w\", existing.ID, err)\n}","preventionTips":["Run only one Linear sync per workspace at a time (file lock or scheduler)","Use exponential backoff on transient storage errors","Verify DB connectivity before long-running sync jobs"],"tags":["linear","storage","update-issue","concurrency"],"backgroundTag":"issue-update-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}