Variable Pool Falsy Value Handling#
A recurring correctness pattern in Dify's workflow engine: implicit Python truthiness checks (if not value:) used as "missing value" guards incorrectly discard valid falsy values β False, 0, and "" β causing those values to silently vanish during variable resolution. The fix in both affected sites is to switch to an explicit is None check.
Two distinct bugs were identified and fixed in separate PRs:
Bug 1 β _get_nested_attribute() in VariablePool#
Original location: api/core/workflow/entities/variable_pool.py β now externalized to the graphon package; VariablePool is imported from graphon.runtime in current HEAD
When resolving a nested attribute on an ObjectSegment (e.g., some_object.count where count = 0), _get_nested_attribute() returned None for any falsy value because dict.get(attr) returns None for both "key absent" and "key present with a None/falsy value."
Fix (PR #26155): Replace obj.get(attr) with an explicit membership test before the get:
# Before
if not isinstance(obj, dict):
return None
return obj.get(attr)
# After
if not isinstance(obj, dict) or attr not in obj:
return None
return variable_factory.build_segment(obj.get(attr))
attr not in obj explicitly distinguishes "key is absent" from "key exists with a falsy value." The companion change in api/factories/variable_factory.py short-circuits build_segment() when the input is already a Segment instance, preventing double-wrapping.
Unit tests covering None, "", 0, and False were added at api/tests/unit_tests/core/workflow/entities/test_variable_pool.py.
Bug 2 β mapping_user_inputs_to_variable_pool() in WorkflowEntry#
File: api/core/workflow/workflow_entry.py β used during single-step (debugger) node execution.
The method falls back from a full key (node_id.variable_key) to a short key (variable_key) when looking up user inputs. As of the current HEAD, the fallback guard at line 519 still reads:
input_value = user_inputs.get(node_variable)
if not input_value: # β bug: also triggers for False, 0, ""
input_value = user_inputs.get(node_variable_key)
if input_value is None:
continue
When user_inputs["node.enabled"] = False, not input_value is True, so the code falls through to the short-key lookup. If that key is also absent, input_value becomes None and continue skips writing the value to the pool β even though the caller provided an explicit False.
Fix (PR #25908) changed the fallback guard to an explicit None check:
if input_value is None: # only falls back when key is truly absent
input_value = user_inputs.get(node_variable_key)
β οΈ Regression risk: Current HEAD (
bec2c678) showsif not input_value:at line 519 β the same anti-pattern that PR #25908 intended to fix. This is either a regression or an incomplete migration.
The Anti-Pattern and Correct Alternatives#
| Anti-pattern | Correct pattern | What it fixes |
|---|---|---|
if not value: | if value is None: | Keeps False, 0, "" from being treated as absent |
obj.get(key) truthiness | key not in obj + obj.get(key) | Distinguishes missing key from falsy value |
If a workflow drops 0, False, or "" mid-execution, audit any if not value: or if value: gate that controls variable pool writes or reads. The method comment in workflow_entry.py itself warns that the semantics of mapping_user_inputs_to_variable_pool are not clearly defined and that modifications carry regression risk .
Key Source References#
| Source | Purpose |
|---|---|
api/core/workflow/workflow_entry.py lines 480β553 | mapping_user_inputs_to_variable_pool() β active bug site |
api/core/workflow/entities/variable_pool.py (now in graphon) | _get_nested_attribute() β fixed in PR #26155 |
api/factories/variable_factory.py | build_segment() β updated to guard against double-wrapping |
| PR #26155 | Fix nested attribute falsy value bug |
| PR #25908 | Fix single-step variable loading falsy input bug |