Misleading "does not have valid YAML syntax" error when a nested include's include:rules filter out all of its includes

Summary

When an included file contains only include: entries that are all removed by their include:rules: (and no other keys), that file expands to an empty hash. The parent then reports a misleading error:

Included file `<parent>.yml` does not have valid YAML syntax!

The YAML is perfectly valid — the real cause is that include:rules: filtered out every nested include, leaving the file empty.

Steps to reproduce

.gitlab-ci.yml:

include:
  - local: nested.yml

nested.yml:

include:
  - local: .child.yml
    rules:
      - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Run a push pipeline. The .child.yml include is filtered out (its rule only matches merge_request_event), so nested.yml expands to an empty hash.

Expected

Either a clear message indicating the include resolved to nothing because of include:rules: filtering, or the empty nested include treated as an empty (no-op) inclusion — not a "valid YAML syntax" error.

Actual

Included file `.gitlab-ci.yml` does not have valid YAML syntax!

The pipeline is persisted as a failed config_error.

Root cause

In Gitlab::Ci::Config::External::Processor#perform (lib/gitlab/ci/config/external/processor.rb), when all includes of a file are filtered out, @external_files is empty and the method early-returns @values with the include: keyword still present:

def perform
  return @values if @external_files.empty?
  # ... merge, append inline, remove_include_keyword!, validate
end

Downstream, File::Base#validate_hash! sees to_hash as blank and raises the "does not have valid YAML syntax" error (lib/gitlab/ci/config/external/file/base.rb).

Notes

  • This is pre-existing behavior on master, independent of !246515 (merged) (which handles the top-level "no visible jobs" case). Confirmed by reproducing against a clean master lib.
  • Related: #591780 and !246515 (merged).