Summary
In a workflow that uses both main (for minor cuts) and long-lived release branches (for patch cuts), linear-release sync (CLI v0.10.0) silently skips release creation on the next main cut, leaving the release entry absent. A subsequent update --stage=... call then fails with No release found with version "X.Y.Z" for pipeline "<uuid>".
The root cause is that the auto-detected base commit, after a release-branch patch sync, is no longer an ancestor of main HEAD. The ancestor walk appears to only consider the top 2 most-recent releases, so once enough patches have been synced from a release branch, the older main-line release falls out of the candidate window. sync then falls back to the "first-sync scan boundary," diffs HEAD..HEAD, finds 0 commits, and returns {"release":null} without creating the release.
There's no CLI flag (--from-commit, --base-commit, --previous-version, etc.) to override this.
Repro
Single Linear pipeline tracking a monorepo. Standard main + long-lived release branches workflow with patch-version increments — no pre-release / rc tags involved.
- On
main at commit A, run sync --release-version=1.52.0. Linear creates release 1.52.0 with tracked commit A.
- Branch
release/1.52 from A.
- Land a hotfix on
release/1.52 at commit B (lives only on release/1.52; B is not an ancestor of future main). Run sync --release-version=1.52.1 from release/1.52. Linear creates release 1.52.1 with tracked commit B.
- (Optionally a 2nd hotfix at commit
B2, synced as 1.52.2, etc.)
- Continue work on
main. Weeks later, on main at commit C, run sync --release-version=1.53.0. A is an ancestor of C; B (and B2, ...) is not.
Expected: release 1.53.0 is created with commits between A (most recent main-ancestor release) and C.
Actual output:
=> Using custom release version: 1.53.0
=> No recent release is an ancestor of <C> (2 candidates considered); falling back to the first-sync scan boundary
=> Found 0 commits between <C> and <C>
=> No commits found matching [<include-paths>]. Skipping release creation.
{"release":null}
A follow-up update --release-version=1.53.0 --stage="<some stage>" then fails:
Error: Failed to update release: Not found - No release found with version "1.53.0" for pipeline "<uuid>".
This worked on CLI v0.7.0 (no ancestor check; it just created the release using whatever range it had).
Why this matters
The "main for minor cuts, branch for patches" pattern is common in long-lived monorepos (and is the only sane way to land a hotfix without dragging in everything that landed on main since the cut). Under v0.10.0, once one or two patch syncs from a release branch push the main-line release out of the 2-candidate ancestor window, the next minor sync from main fails open — and the next update call fails closed.
Requests
Any of these would fix it cleanly:
- Add a
--from-commit / --base-commit / --previous-version flag so the caller can say "diff from <sha>" or "diff from release 1.52.0's commit." Lets the workflow be explicit and avoids the ancestor-check gamble.
- Walk further back when no recent release is an ancestor of HEAD — page through older releases until one matches, instead of stopping at 2.
- At minimum, don't silently skip release creation: if no candidate matches as an ancestor and the auto-detected range is empty, exit non-zero so the workflow surfaces the misconfiguration rather than failing on the next
update step.
Happy to provide more context if useful.
Summary
In a workflow that uses both
main(for minor cuts) and long-lived release branches (for patch cuts),linear-release sync(CLI v0.10.0) silently skips release creation on the nextmaincut, leaving the release entry absent. A subsequentupdate --stage=...call then fails withNo release found with version "X.Y.Z" for pipeline "<uuid>".The root cause is that the auto-detected base commit, after a release-branch patch sync, is no longer an ancestor of
mainHEAD. The ancestor walk appears to only consider the top 2 most-recent releases, so once enough patches have been synced from a release branch, the oldermain-line release falls out of the candidate window.syncthen falls back to the "first-sync scan boundary," diffsHEAD..HEAD, finds 0 commits, and returns{"release":null}without creating the release.There's no CLI flag (
--from-commit,--base-commit,--previous-version, etc.) to override this.Repro
Single Linear pipeline tracking a monorepo. Standard
main+ long-lived release branches workflow with patch-version increments — no pre-release / rc tags involved.mainat commitA, runsync --release-version=1.52.0. Linear creates release1.52.0with tracked commitA.release/1.52fromA.release/1.52at commitB(lives only onrelease/1.52;Bis not an ancestor of futuremain). Runsync --release-version=1.52.1fromrelease/1.52. Linear creates release1.52.1with tracked commitB.B2, synced as1.52.2, etc.)main. Weeks later, onmainat commitC, runsync --release-version=1.53.0.Ais an ancestor ofC;B(andB2, ...) is not.Expected: release
1.53.0is created with commits betweenA(most recentmain-ancestor release) andC.Actual output:
A follow-up
update --release-version=1.53.0 --stage="<some stage>"then fails:This worked on CLI v0.7.0 (no ancestor check; it just created the release using whatever range it had).
Why this matters
The "main for minor cuts, branch for patches" pattern is common in long-lived monorepos (and is the only sane way to land a hotfix without dragging in everything that landed on
mainsince the cut). Under v0.10.0, once one or two patch syncs from a release branch push the main-line release out of the 2-candidate ancestor window, the next minor sync frommainfails open — and the nextupdatecall fails closed.Requests
Any of these would fix it cleanly:
--from-commit/--base-commit/--previous-versionflag so the caller can say "diff from<sha>" or "diff from release1.52.0's commit." Lets the workflow be explicit and avoids the ancestor-check gamble.updatestep.Happy to provide more context if useful.