Skip to content

fix(ui): render forward-referenced DAG dependencies in Graph Viewer#16295

Open
myzk-a wants to merge 3 commits into
argoproj:mainfrom
myzk-a:fix/graphviewer-dag-genre-crash
Open

fix(ui): render forward-referenced DAG dependencies in Graph Viewer#16295
myzk-a wants to merge 3 commits into
argoproj:mainfrom
myzk-a:fix/graphviewer-dag-genre-crash

Conversation

@myzk-a

@myzk-a myzk-a commented Jun 24, 2026

Copy link
Copy Markdown

Supersedes #16119

Motivation

Opening the Graph tab for a Workflow / WorkflowTemplate / ClusterWorkflowTemplate / CronWorkflow whose DAG contains a forward reference crashes the UI with:

Cannot read properties of undefined (reading 'genre')

A forward reference is a DAG task that depends on a sibling task defined later in the tasks array. Argo's controller does not require tasks to be declared in topological order, so these workflows run perfectly fine — but the Graph tab is unusable for them.

populateGraphFromWorkflow builds the graph in a single pass: for each task it creates the node and then immediately resolves that task's dependencies. When a task references a dependency that has not been created yet (forward reference, or a name that does not match a sibling task), graph.nodes.get(dependancyName) returns undefined and reading .genre on it throws.

Reproduction

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: dag-forward-reference
spec:
  entrypoint: main
  templates:
    - name: main
      dag:
        tasks:
          # deploy is declared first but depends on build (declared below) = forward reference
          - name: deploy
            depends: "build"
            template: echo
          - name: build
            template: echo
    - name: echo
      container:
        image: alpine:3.18
        command: [echo]
        args: ["hello"]

Save this template and open its Graph tab → the view crashes with reading 'genre'.

Modifications

  • In populateGraphFromWorkflow (ui/src/shared/components/editors/graph-viewer.tsx), resolve the dependency's genre from the template model (template.dag.tasks + getTaskGenre) instead of from the partially-built graph.
    • This is order-independent, so the dependency edge is created correctly even for forward references — and it matches the exact genre value that createNode stores for that task.
  • Added a regression test asserting that the forward-referenced edge (build → deploy) is created.

Why not the guard approach (#16119)?

#16119 guards the undefined lookup and returns early, which stops the crash but drops the forward-reference edge, leaving an orphan node (the dependent task has no incoming edge). Resolving the genre from the model fixes the crash and renders the edge correctly, so it is strictly better for this case.

Verification

  • cd ui && yarn jest graph-viewer → 25 passed (includes the new forward-reference edge test).
  • cd ui && yarn lint (eslint + tsc) → clean.
  • Verified the new test goes red on current main (reading 'genre' throw) and on fix(ui): guard missing DAG dependency nodes in GraphViewer #16119's guard approach (missing build → deploy edge), and green with this change.
  • Manually verified in the running UI (make start UI=true): applied the reproduction WorkflowTemplate above, opened its Graph tab. Before the fix the tab crashed with Cannot read properties of undefined (reading 'genre'); after the fix the graph renders and the build → deploy edge is drawn correctly (screenshot below).

Graph tab after the fix (the build → deploy edge is rendered):
image

Documentation

Not needed — this is a bug fix for existing behavior with no user-facing API or configuration change.

AI

Generative AI (Claude Code) was used as a pair-programming assistant to investigate the root cause, weigh fix approaches, and draft this PR description and the commit message. The code change and the test were authored and reviewed by me.

Summary by CodeRabbit

  • Bug Fixes
    • Improved workflow graph rendering so task dependencies are connected correctly, including cases where a referenced task appears later in the workflow definition.
    • Updated DAG edge creation to determine dependency task types more reliably when building graph connections.
  • Tests
    • Added coverage to ensure forward-referenced DAG dependencies produce the expected graph edges.
    • Expanded graph viewer tests to include additional workflow-related types.

Signed-off-by: myzk-a <rorosocksxion@gmail.com>
@myzk-a
myzk-a marked this pull request as ready for review June 24, 2026 22:23
@myzk-a
myzk-a requested a review from a team as a code owner June 24, 2026 22:23
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2f0cb679-ee18-45b1-bb61-a46e360e6dba

📥 Commits

Reviewing files that changed from the base of the PR and between e1cf957 and c39a815.

📒 Files selected for processing (1)
  • ui/src/shared/components/editors/graph-viewer.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • ui/src/shared/components/editors/graph-viewer.tsx

📝 Walkthrough

Walkthrough

The DAG edge-building logic now resolves dependency task genre from template.dag.tasks instead of existing graph nodes, and the test suite adds coverage for a forward-referenced dependency case.

Changes

Forward-reference dependency fix

Layer / File(s) Summary
Dependency genre lookup fix and test coverage
ui/src/shared/components/editors/graph-viewer.tsx, ui/src/shared/components/editors/graph-viewer.test.tsx
processDAGTemplate now resolves dependency task genre from template.dag.tasks via getTaskGenre, and a new test verifies edge creation when a dependency task is defined later in the workflow template.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is conventional and clearly summarizes the main fix for forward-referenced DAG dependencies.
Description check ✅ Passed The description covers motivation, modifications, verification, documentation, and AI, with a concrete reproduction and test evidence.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
ui/src/shared/components/editors/graph-viewer.tsx (1)

142-146: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Optional: precompute a task-name lookup map to avoid repeated find.

template.dag.tasks.find(...) runs for every dependency of every task (O(n·m)). Building a Map<string, DAGTask> once per DAG template (e.g., alongside templateMap/templateLeafMap setup) would avoid the repeated linear scans. Given typical DAG task counts this is low-impact, but worth considering for larger workflows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/shared/components/editors/graph-viewer.tsx` around lines 142 - 146,
The dependency lookup in the graph viewer is doing a repeated linear search for
every dependency via template.dag.tasks.find, which can become expensive for
larger DAGs. Build and reuse a Map<string, DAGTask> for each DAG template during
the existing template setup (near templateMap/templateLeafMap initialization),
then update the dependency handling in the graph-viewer logic that uses
dependencies.forEach to read depTask from the map instead of scanning the array.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@ui/src/shared/components/editors/graph-viewer.tsx`:
- Around line 142-146: The dependency lookup in the graph viewer is doing a
repeated linear search for every dependency via template.dag.tasks.find, which
can become expensive for larger DAGs. Build and reuse a Map<string, DAGTask> for
each DAG template during the existing template setup (near
templateMap/templateLeafMap initialization), then update the dependency handling
in the graph-viewer logic that uses dependencies.forEach to read depTask from
the map instead of scanning the array.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3a8097c6-995c-4feb-9463-6dbf9af39c13

📥 Commits

Reviewing files that changed from the base of the PR and between 7fefac7 and e1cf957.

📒 Files selected for processing (2)
  • ui/src/shared/components/editors/graph-viewer.test.tsx
  • ui/src/shared/components/editors/graph-viewer.tsx

Signed-off-by: myzk-a <rorosocksxion@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant