Skip to content

failedWorkflow input to failure workflow is a raw WorkflowModel -- nested ${...} path resolution silently returns null #1164

Description

@NicolasBissig

Describe the bug

When a workflow fails and triggers its failureWorkflow, Conductor puts the failed workflow object into the failure workflow's input under the key failedWorkflow. This object is a raw WorkflowModel Java instance, not a plain Map. As a result:

  • ${workflow.input.failedWorkflow} resolves correctly, the full object is returned and serialized to JSON on persistence.
  • ${workflow.input.failedWorkflow.someField} always resolves to null. JsonPath (using JsonSmartJsonProvider) cannot traverse into a Java POJO, and the error is silently swallowed by SUPPRESS_EXCEPTIONS.

The data is present. The path expression is correct. The failure is at the POJO boundary inside JsonPath.

Details

Conductor version: v3.30.2
Persistence: Postgres
Queue: Postgres

To Reproduce

  1. Define a workflow with "failureWorkflow": "My Failure Workflow".
  2. Define My Failure Workflow with a task whose inputParameters include:
    "workflowName": "${workflow.input.failedWorkflow.workflowName}"
  3. Trigger the parent workflow and cause it to fail.
  4. Observe that workflowName in the failure workflow task input is null.

To confirm the data is present, add a second parameter:

"fullWorkflow": "${workflow.input.failedWorkflow}"

This resolves to the complete workflow JSON including workflowName, proving the object is accessible, but nested path traversal into it fails.

Root cause

WorkflowExecutorOps.java, method terminateWorkflow(), around line 768:

Map<String, Object> input = new HashMap<>(workflow.getInput());
input.put("workflowId", workflowId);
input.put("reason", reason);
input.put("failureStatus", workflow.getStatus().toString());
input.put("failedWorkflow", workflow);   // <-- raw WorkflowModel, not a Map

ParametersUtils.getTaskInputV2() then calls:

Configuration option = Configuration.defaultConfiguration().addOptions(Option.SUPPRESS_EXCEPTIONS);
DocumentContext documentContext = JsonPath.parse(inputMap, option);

JsonSmartJsonProvider (the default) cannot navigate into a Java POJO. When the path resolver reaches failedWorkflow and attempts to traverse further, it fails silently and returns null.

Expected behavior

${workflow.input.failedWorkflow.workflowName} and all other nested paths into failedWorkflow should resolve to the actual field values of the failed workflow.

Suggested fix

Serialize workflow to a plain Map before inserting it into the failure workflow input:

input.put("failedWorkflow", objectMapper.convertValue(workflow, Map.class));

Additional context

SUPPRESS_EXCEPTIONS in ParametersUtils.java makes this failure completely silent, no log line, no exception. The task simply receives null. This makes the bug very hard to diagnose without side-by-side comparison of ${workflow.input.failedWorkflow} (whole object, works) vs ${workflow.input.failedWorkflow.someField} (nested, null).

The same root cause affects GraalJS INLINE tasks: when failedWorkflow is forwarded via inputParameters, GraalJS receives the raw WorkflowModel as a Java interop object. Property access ($.failedWorkflow.workflowName) returns undefined because GraalJS does not automatically call bean getters under the configured HostAccess policy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions