Skip to content

Fix empty cycle point bug#2386

Open
MetRonnie wants to merge 3 commits intocylc:masterfrom
MetRonnie:empty-cycle-point
Open

Fix empty cycle point bug#2386
MetRonnie wants to merge 3 commits intocylc:masterfrom
MetRonnie:empty-cycle-point

Conversation

@MetRonnie
Copy link
Member

@MetRonnie MetRonnie commented Dec 16, 2025

Closes #1922

Guard against node.ancestors being undefined rather than an empty array.

Check List

  • I have read CONTRIBUTING.md and added my name as a Code Contributor.
  • Contains logically grouped changes (else tidy your branch by rebase).
  • Does not contain off-topic changes (use other PRs for other changes).
  • Tests not needed?
  • Changelog entry
  • Docs not needed

@MetRonnie MetRonnie added this to the 2.12.0 milestone Dec 16, 2025
@MetRonnie MetRonnie self-assigned this Dec 16, 2025
@MetRonnie MetRonnie added bug Something isn't working small labels Dec 16, 2025
@oliver-sanders oliver-sanders modified the milestones: 2.12.0, 2.13.0 Dec 18, 2025
Comment on lines +389 to 396
for (let i = node.ancestors?.length ?? 0; i > 0; i--) {
const { name } = node.ancestors[i - 1]
ret.push([
'family',
ancestor.name,
lastTokens.clone({ task: ancestor.name })
name,
lastTokens.clone({ task: name })
])
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, this suggests that we are receiving an added delta for the family, in which ancestors is undefined? If so, we have a data store bug :(

Note, this is also relevant to the graph view which needs to request the family hierarchy.

By this logic:

  • If ancestors is undefined on the first delta we receive, then we won't create the families under the cycle point.
  • If ancestors is defined on a subsequent delta, that won't trigger this to subsequently run (because the node already exists in the store).

This will certainly give us a partial tree at first, but I'm not sure if something else in the store will subsequently force these nodes to be created on future deltas, so the issue might get fixed in time.

Copy link
Member Author

@MetRonnie MetRonnie Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there are any ancestors that are actually missing in the data. I've captured this in the wild running with the site default stock UI server. The delta in question seems to be an updated delta:

{
  "id": "3",
  "type": "data",
  "payload": {
    "data": {
      "deltas": {
        "added": {
          "familyProxies": [],
          "taskProxies": [],
          "jobs": []
        },
        "updated": {
          "workflow": { "id": "~user/wflow" },
          "familyProxies": [
            {
              "id": "~user/wflow//1/root",
              "state": "running",
              "childTasks": [
                { "id": "~user/wflow//1/one" },
                { "id": "~user/wflow//1/foo" },
                ...
              ]
            }
          ],
          "taskProxies": [...],
          "jobs": [...]
        },
        "pruned": {
          "familyProxies": [],
          "taskProxies": [],
          "jobs": []
        },
        "id": "~user/wflow"
      }
    }
  }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is missing is an added delta containing the familyProxies before the the updated delta

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so this fix would disable the logic which populates these entries then?

(the logic only kicks in when a node is created, not when it is updated?)

Copy link
Member Author

@MetRonnie MetRonnie Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the logic kicks in if an update is received but the family proxy is not yet in the index (as is the case with the delta above)

let treeItem = getIndex(state, id)
if (treeItem) {
// node already exists -> update it
collateState(state, treeItem, updatedData, false)
Object.assign(treeItem.node, updatedData)
applyInheritance(state, treeItem)
return
}
// create a new tree item
let treeParent
[treeParent, treeItem] = createTreeNode(state, id, tokens, updatedData)

Sounds like this situation of missing an added delta is a cylc-flow/cylc-uiserver bug. In the meantime, is this UI fix good to go for 2.12.0?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(corrected understanding of the problem)

  • There was no added delta for the FamilyProxy.
  • Only an updated delta which did not have ancestors defined (which is reasonable for an updated delta).

Does this mean:

  • The added delta just never got sent, something was lost in translation (family should be added to the store).
  • The family was created and pruned, but an updated delta slipped through the net (family should not be added to the store).

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Discussed in person after some digging, summarising here for the record)

Does this mean: The added delta just never got sent, something was lost in translation (family should be added to the store).

Yes, that. Possibly fixed by cylc/cylc-uiserver#761

N.B. updated deltas for root families will never have ancestors as we have updated(stripNull = true) in the tree view subscription.

Btw, this PR will also defend against the bug if stripNull is ever turned on for added deltas

Copy link
Member

@oliver-sanders oliver-sanders Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that. Possibly fixed by cylc/cylc-uiserver#761

Hard to confirm this intermittent bug I appreciate, it sounds like this issue may have been fixed, do we think we still need this?


Btw, this PR will also defend against the bug if stripNull is ever turned on for added deltas

The stripNull option only makes sense in the context of updated deltas (shouldn't be an option for added) so we don't need to be robust against this (note the stripNull = false default is enforced by subscription merging so can't be done by accident).

Copy link
Member Author

@MetRonnie MetRonnie Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know if it has been fixed by cylc/cylc-uiserver#761, and I don't think this PR will do any harm even if it has already been fixed. I've asked David Sutherland if he has any insight - cylc/cylc-uiserver#767

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is skipping some logic in an event which (by contract) should not be possible.

So I would be keen to avoid this workaround if possible (not 100% convinced on the won't do harm bit).

@MetRonnie MetRonnie modified the milestones: 2.13.0, 2.12.0 Dec 18, 2025
@oliver-sanders oliver-sanders modified the milestones: 2.12.0, 2.13.0 Jan 7, 2026
@MetRonnie MetRonnie closed this Jan 22, 2026
@MetRonnie MetRonnie removed this from the 2.13.0 milestone Jan 22, 2026
@MetRonnie
Copy link
Member Author

Just experienced this in the wild again unfortunately

cylc-flow 8.6.3
cylc-uiserver 1.8.4
cylc-ui 2.13.0

@MetRonnie MetRonnie reopened this Mar 6, 2026
@MetRonnie MetRonnie requested a review from oliver-sanders March 6, 2026 13:04
@MetRonnie MetRonnie added this to the 2.14.0 milestone Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty cycle point - getFamilyTree() TypeError

2 participants