dify icon indicating copy to clipboard operation
dify copied to clipboard

Unexpected behaviour of Variable Aggregator on different branches

Open kirakiseki opened this issue 8 months ago • 3 comments

Self Checks

  • [x] This is only for bug report, if you would like to ask a question, please head to Discussions.
  • [x] I have searched for existing issues search for existing issues, including closed ones.
  • [x] I confirm that I am using English to submit this report (我已阅读并同意 Language Policy).
  • [x] [FOR CHINESE USERS] 请务必使用英文提交 Issue,否则会被关闭。谢谢!:)
  • [x] Please do not modify this template :) and fill in all the required fields.

Dify version

1.4.1

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

I found a strange behaviour in my chatflow. I want to search different knowledge base depends on different event types and use Variable Aggregator to aggregate corresponding knowledge. But the Variable Aggregator are skipped like this:

Image

I made a minimal reproduction chatflow like this:

Image

The Conditional Branch node would go into different branches depending on user's input from choices [1,2,3]. The Code Execution nodes 1A, 1B, 2A, 2B are used to generate its corresponding strings like "1a", "1b", "2a" and "2b". The Variable Aggregator nodes A and B where the bug lies aim to aggregate the results with postfix "a" and "b". The Answer node would output the results from the both Variable Aggregator nodes directly.

Here is the minium reproduction DSL for debugging:

DSL content
app:/
  description: ''
  icon: 🤖
  icon_background: '#FFEAD5'
  mode: advanced-chat
  name: bugtest-并行
  use_icon_as_answer_icon: false
dependencies: []
kind: app
version: 0.3.0
workflow:
  conversation_variables: []
  environment_variables: []
  features:
    file_upload:
      allowed_file_extensions:
      - .JPG
      - .JPEG
      - .PNG
      - .GIF
      - .WEBP
      - .SVG
      allowed_file_types:
      - image
      allowed_file_upload_methods:
      - local_file
      - remote_url
      enabled: false
      fileUploadConfig:
        audio_file_size_limit: 50
        batch_count_limit: 5
        file_size_limit: 15
        image_file_size_limit: 10
        video_file_size_limit: 100
        workflow_file_upload_limit: 10
      image:
        enabled: false
        number_limits: 3
        transfer_methods:
        - local_file
        - remote_url
      number_limits: 3
    opening_statement: ''
    retriever_resource:
      enabled: true
    sensitive_word_avoidance:
      enabled: false
    speech_to_text:
      enabled: false
    suggested_questions: []
    suggested_questions_after_answer:
      enabled: false
    text_to_speech:
      enabled: false
      language: ''
      voice: ''
  graph:
    edges:
    - data:
        isInLoop: false
        sourceType: start
        targetType: if-else
      id: 1748962309200-source-1748962316650-target
      selected: false
      source: '1748962309200'
      sourceHandle: source
      target: '1748962316650'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: if-else
        targetType: code
      id: 1748962316650-true-1748962370039-target
      selected: false
      source: '1748962316650'
      sourceHandle: 'true'
      target: '1748962370039'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: variable-aggregator
        targetType: answer
      id: 1748962354160-source-1748962443930-target
      selected: false
      source: '1748962354160'
      sourceHandle: source
      target: '1748962443930'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: if-else
        targetType: code
      id: 1748962316650-true-17489624177180-target
      selected: false
      source: '1748962316650'
      sourceHandle: 'true'
      target: '17489624177180'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: if-else
        targetType: code
      id: 1748962316650-f7644790-79ca-4c3f-b690-b85450789c77-17489625222390-target
      selected: false
      source: '1748962316650'
      sourceHandle: f7644790-79ca-4c3f-b690-b85450789c77
      target: '17489625222390'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: if-else
        targetType: code
      id: 1748962316650-f7644790-79ca-4c3f-b690-b85450789c77-17489625325210-target
      selected: false
      source: '1748962316650'
      sourceHandle: f7644790-79ca-4c3f-b690-b85450789c77
      target: '17489625325210'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: code
        targetType: variable-aggregator
      id: 1748962370039-source-1748962354160-target
      selected: false
      source: '1748962370039'
      sourceHandle: source
      target: '1748962354160'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: code
        targetType: variable-aggregator
      id: 17489625222390-source-1748962354160-target
      selected: false
      source: '17489625222390'
      sourceHandle: source
      target: '1748962354160'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: code
        targetType: variable-aggregator
      id: 17489624177180-source-17489625685610-target
      selected: false
      source: '17489624177180'
      sourceHandle: source
      target: '17489625685610'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: code
        targetType: variable-aggregator
      id: 17489625325210-source-17489625685610-target
      selected: false
      source: '17489625325210'
      sourceHandle: source
      target: '17489625685610'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: variable-aggregator
        targetType: answer
      id: 17489625685610-source-1748962443930-target
      selected: false
      source: '17489625685610'
      sourceHandle: source
      target: '1748962443930'
      targetHandle: target
      type: custom
      zIndex: 0
    nodes:
    - data:
        desc: ''
        selected: false
        title: 开始
        type: start
        variables: []
      height: 54
      id: '1748962309200'
      position:
        x: 80
        y: 282
      positionAbsolute:
        x: 80
        y: 282
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        cases:
        - case_id: 'true'
          conditions:
          - comparison_operator: is
            id: 5ddebef1-e5cf-4043-8101-1dd0b9c7a472
            value: '1'
            varType: string
            variable_selector:
            - sys
            - query
          id: 'true'
          logical_operator: and
        - case_id: f7644790-79ca-4c3f-b690-b85450789c77
          conditions:
          - comparison_operator: is
            id: 274c03d5-ffec-471b-a11d-9c14c5c8593d
            value: '2'
            varType: string
            variable_selector:
            - sys
            - query
          id: f7644790-79ca-4c3f-b690-b85450789c77
          logical_operator: and
        - case_id: ea89f090-bd49-467e-980c-943312d36439
          conditions:
          - comparison_operator: is
            id: 73e44731-75c7-4a38-ae73-29bacfa247e8
            value: '3'
            varType: string
            variable_selector:
            - sys
            - query
          id: ea89f090-bd49-467e-980c-943312d36439
          logical_operator: and
        desc: ''
        selected: false
        title: 条件分支
        type: if-else
      height: 222
      id: '1748962316650'
      position:
        x: 386
        y: 282
      positionAbsolute:
        x: 386
        y: 282
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        desc: ''
        output_type: string
        selected: false
        title: a
        type: variable-aggregator
        variables:
        - - '1748962370039'
          - result
        - - '17489625222390'
          - result
      height: 131
      id: '1748962354160'
      position:
        x: 1238
        y: 203
      positionAbsolute:
        x: 1238
        y: 203
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        code: "\ndef main(arg1: str) -> dict:\n    return {\n        \"result\": \"\
          1a\",\n    }\n"
        code_language: python3
        desc: ''
        outputs:
          result:
            children: null
            type: string
        selected: true
        title: 1a
        type: code
        variables:
        - value_selector:
          - sys
          - query
          variable: arg1
      height: 54
      id: '1748962370039'
      position:
        x: 729
        y: 175
      positionAbsolute:
        x: 729
        y: 175
      selected: true
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        code: "\ndef main(arg1: str) -> dict:\n    return {\n        \"result\": \"\
          1b\",\n    }\n"
        code_language: python3
        desc: ''
        outputs:
          result:
            children: null
            type: string
        selected: false
        title: 1b
        type: code
        variables:
        - value_selector:
          - sys
          - query
          variable: arg1
      height: 54
      id: '17489624177180'
      position:
        x: 729
        y: 267
      positionAbsolute:
        x: 729
        y: 267
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: 'This is Answer Node


          {{#1748962354160.output#}}

          {{#17489625685610.output#}}'
        desc: ''
        selected: false
        title: 直接回复
        type: answer
        variables: []
      height: 121
      id: '1748962443930'
      position:
        x: 1625.9925571196463
        y: 310.910308834967
      positionAbsolute:
        x: 1625.9925571196463
        y: 310.910308834967
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        code: "\ndef main(arg1: str) -> dict:\n    return {\n        \"result\": \"\
          2a\",\n    }\n"
        code_language: python3
        desc: ''
        outputs:
          result:
            children: null
            type: string
        selected: false
        title: 2a
        type: code
        variables:
        - value_selector:
          - sys
          - query
          variable: arg1
      height: 54
      id: '17489625222390'
      position:
        x: 729
        y: 396.5
      positionAbsolute:
        x: 729
        y: 396.5
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        code: "\ndef main(arg1: str) -> dict:\n    return {\n        \"result\": \"\
          2b\",\n    }\n"
        code_language: python3
        desc: ''
        outputs:
          result:
            children: null
            type: string
        selected: false
        title: 2b
        type: code
        variables:
        - value_selector:
          - sys
          - query
          variable: arg1
      height: 54
      id: '17489625325210'
      position:
        x: 729
        y: 481.5
      positionAbsolute:
        x: 729
        y: 481.5
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        desc: ''
        output_type: string
        selected: false
        title: b
        type: variable-aggregator
        variables:
        - - '17489624177180'
          - result
        - - '17489625325210'
          - result
      height: 131
      id: '17489625685610'
      position:
        x: 1238
        y: 396.5
      positionAbsolute:
        x: 1238
        y: 396.5
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    viewport:
      x: -189.64043824692612
      y: 15.075408994011923
      zoom: 0.7453551933994609

✔️ Expected Behavior

For CASE 1 branch of Conditional Branch node, triggers Code Execution node 1A and 1B, and Variable Aggregator node A takes the first None value that is "1a", while Variable Aggregator node B outputs "1b", then the result in the Answer node should be "1a 1b".

So does the CASE 2 branch, the result of Variable Aggregator node A is "2a", node B is "2b", final answer should be "2a2b".

❌ Actual Behavior

When it goes into CASE 1 branch, after the execution of 1A and 1B, Variable Aggregator A and B are both skipped, it just slipped into the final Answer Node with no expected output "1a1b". Image

But it's weird when it's in CASE 2, everything runs well as my expection, the answer is "2a2b" normally.

Image

If I add an other node like Code Execution node A+B which is used to concat the output from Variable Aggregator nodes A and B, it would throw an unexpected keyword argument error.

Image

kirakiseki avatar Jun 03 '25 15:06 kirakiseki

Hi @kirakiseki! I'm Dosu and I’m helping the dify team.

Based on your detailed report and the minimal reproduction DSL, here’s what’s happening:

The Variable Aggregator node in Dify is designed to consolidate variables from multiple branches, but it only aggregates the first non-None variable from the selectors you provide. It does not merge results from all branches—only the first available one is used for each aggregator. This is by design and is documented as a limitation: the aggregator is not suitable for collecting all results from multiple workflow branches, and will not combine outputs from parallel or conditional branches into a single output unless each branch is guaranteed to produce a value for the aggregator to pick up. If a branch does not execute (or its output is None), the aggregator may be skipped or produce no output for that path (reference, reference).

In your CASE 1, it appears that the Variable Aggregator nodes are skipped because the expected variables are not present in the variable pool at the time the aggregator runs, likely due to the execution path or how the branches are merged. This can happen if the workflow graph does not guarantee that both code nodes (1A and 1B) always execute and produce outputs before the aggregator runs, or if the aggregator’s selectors do not match the actual outputs produced in that branch.

In contrast, CASE 2 works as expected because both code nodes (2A and 2B) are executed and their outputs are available for aggregation, matching the selectors configured in the aggregator.

If you need to collect and combine results from multiple branches, consider using a Code node or Template node after the branches to explicitly merge the outputs, rather than relying on the Variable Aggregator node (reference). Also, ensure that your workflow graph connects all relevant branches to the aggregator and that the selectors match the outputs produced in each branch.

If you encounter an "unexpected keyword argument" error when adding a Code node to concatenate aggregator outputs, double-check the variable names and ensure that the outputs from the aggregator nodes are correctly referenced in the downstream node’s input configuration.

In summary: the Variable Aggregator node will only pick up the first available variable from its selectors, and may be skipped if none are present. For true merging of results from multiple branches, use a Code or Template node to combine outputs explicitly.

To reply, just mention my name @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

dosubot[bot] avatar Jun 03 '25 15:06 dosubot[bot]

But why the Case 2 can work normally while the Case 1 doesn't though they have the same structure behind?

kirakiseki avatar Jun 03 '25 16:06 kirakiseki

Update: Question Classifier node also has this problem.

Image

BTW, if anyone is working on or tracking this issue, feel free to reach me out for further infomation or any kind of help, thanks! :)

kirakiseki avatar Jun 08 '25 18:06 kirakiseki

Currently, some bugs related to aggregation and graph nodes cannot be addressed yet as we are restructuring the underlying system.

crazywoola avatar Jul 25 '25 15:07 crazywoola

Hi, @kirakiseki. I'm Dosu, and I'm helping the Dify team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You reported that Variable Aggregator nodes are skipped in chatflows with conditional branches, leading to missing aggregated outputs.
  • It was clarified that Variable Aggregators only pick the first non-None variable and do not merge all branch outputs; explicit merging is recommended.
  • You observed a similar issue with the Question Classifier node and questioned the inconsistency.
  • A maintainer noted that related aggregation and graph node bugs are pending due to ongoing system restructuring.
  • The issue remains unresolved as the underlying system changes are still in progress.

Next Steps:

  • Please confirm if this issue is still relevant with the latest version of Dify by commenting here to keep the discussion open.
  • Otherwise, I will automatically close this issue in 15 days.

Thank you for your understanding and contribution!

dosubot[bot] avatar Aug 30 '25 16:08 dosubot[bot]

This issue will be completely fixed in #24068.

Nov1c444 avatar Sep 09 '25 09:09 Nov1c444

Hi, @kirakiseki. I'm Dosu, and I'm helping the Dify team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You reported that Variable Aggregator nodes in Dify 1.4.1 self-hosted via Docker skip branches unexpectedly, causing incorrect aggregation.
  • It was clarified that Variable Aggregators only pick the first non-None variable and do not merge outputs from all branches.
  • You also noted a similar issue with the Question Classifier node.
  • A maintainer mentioned ongoing system restructuring delaying fixes.
  • The issue is set to be fully resolved in PR #24068.

Next Steps:

  • Please confirm if this issue is still relevant with the latest version of Dify; if so, you can keep the discussion open by commenting here.
  • Otherwise, I will automatically close this issue in 15 days.

Thank you for your understanding and contribution!

dosubot[bot] avatar Oct 15 '25 16:10 dosubot[bot]