Ci::CreateDownstreamPipelineWorker fails when pipeline variable has null value and forward:pipeline_variables is true

Summary

When a pipeline is triggered via the API with a variable whose value is null, and the bridge job is configured with forward: pipeline_variables: true, GitLab's variable expansion machinery crashes with an unhandled NoMethodError inside a Sidekiq worker (Ci::CreateDownstreamPipelineWorker). The downstream pipeline is never created and a data integrity failure error is displayed in the UI.

image

Steps to Reproduce

1. Parent pipeline .gitlab-ci.yml must have forward: pipeline_variables: true:

trigger-downstream:
  trigger:
    project: my-group/second-project
    forward:
      pipeline_variables: true

2. Downstream project .gitlab-ci.yml file:

trigger-downstream:
  script:
    - echo "downstream"

3. Trigger the parent pipeline via the API with a null-valued variable:

curl --request POST \
  --header "PRIVATE-TOKEN: $TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "ref": "main",
    "variables": [{ "key": "MY_VAR", "value": null }]
  }' \
  "https://gitlab.com/api/v4/projects/<PROJECT_ID>/pipelines"

4. Observe — the trigger-downstream bridge job fails, the downstream pipeline is never created, and the Sidekiq worker logs a NoMethodError.

Logs:

exception.backtrace": [
        "lib/expand_variables.rb:39:in `replace_with'",
        "lib/expand_variables.rb:10:in `expand'",
        "lib/gitlab/ci/variables/downstream/expandable_variable_generator.rb:20:in `expanded_var_for'",
        "lib/gitlab/ci/variables/downstream/expandable_variable_generator.rb:9:in `for'",
        "lib/gitlab/ci/variables/downstream/generator.rb:71:in `block in build_downstream_variables_from'",
        "lib/gitlab/ci/variables/collection.rb:53:in `block in each'",
        "lib/gitlab/ci/variables/collection.rb:53:in `each'",
        "lib/gitlab/ci/variables/collection.rb:53:in `each'",
        "lib/gitlab/ci/variables/downstream/generator.rb:67:in `flat_map'",
        "lib/gitlab/ci/variables/downstream/generator.rb:67:in `build_downstream_variables_from'",
        "lib/gitlab/ci/variables/downstream/generator.rb:49:in `downstream_pipeline_variables'",
        "lib/gitlab/ci/variables/downstream/generator.rb:35:in `calculate_downstream_variables'",
        "lib/gitlab/ci/variables/downstream/generator.rb:20:in `calculate'",
        "app/models/ci/bridge.rb:267:in `downstream_variables'",
        "app/models/ci/bridge.rb:397:in `child_params'",
        "app/models/ci/bridge.rb:119:in `downstream_pipeline_params'",
        "app/services/ci/create_downstream_pipeline_service.rb:29:in `execute'",
        "app/workers/ci/create_downstream_pipeline_worker.rb:20:in `block in perform'",
        "activesupport (7.2.3) lib/active_support/core_ext/object/try.rb:12:in `try'",
        "app/workers/ci/create_downstream_pipeline_worker.rb:17:in `perform'",

Current Behavior

meta.root_caller_id: PUT /api/:version/jobs/:id
meta.caller_id: PipelineProcessWorker,
class: Ci::CreateDownstreamPipelineWorker
exception.class: NoMethodError
exception.message: undefined method `gsub' for nil

The Sidekiq worker (Ci::CreateDownstreamPipelineWorker) crashes.

Expected Behavior

The system should either:

  • Detect the null value early and fail gracefully with a clear error message visible on the bridge job in the UI, or
  • Silently skip/drop nil-valued variables before they reach the variable expander

Workaround

Remove forward: pipeline_variables: true from the bridge job configuration, or ensure that all variables passed via the API trigger have non-null string values.

Environment

  • Reproducible in GitLab.com and Self-Managed instances
Edited by 🤖 GitLab Bot 🤖