Skip to content

[Bug]: cp._client() raises LoaderError (KeyError: '__file_client__') when __file_client__ is not packed (resource_modules loader) #69734

Description

@dwoz

What happened?

On a minion whose execution-module dictionary (__salt__) is built by
salt.loader.resource_modules() — the resource-managed minion path on the
development/master branch (Salt Resources) — any call routed through
salt.modules.cp._client() fails. That includes cp.cache_file and therefore
every salt:// fetch a state performs (e.g. a file.managed with a
salt:// source, pkg.installed from a cached package, etc.). The failure is:

KeyError: '__file_client__'

surfaced as a salt.exceptions.LoaderError.

Root cause

cp._client() is:

def _client():
    if __file_client__:
        return __file_client__.value()
    return salt.fileclient.get_file_client(__opts__.value())

The resource_modules loader does not pack a __file_client__ named context.
Because __file_client__ is a loader-managed context variable, evaluating it
(if __file_client__:) when the key was never packed raises LoaderError
instead of returning a falsey value
. That raise short-circuits the intended
fallback, so the function never reaches get_file_client(__opts__...).

minion_mods()-built __salt__ is unaffected because that loader packs
__file_client__ (see "Populate __file_client__ for states" / "Add
__file_client__ for execution modules"). Only loaders that omit the pack
(such as resource_modules) hit this.

Steps to reproduce

  1. Build __salt__ via salt.loader.resource_modules(...) without passing a
    file client (so __file_client__ is not packed).
  2. From a state — or directly — call anything that goes through
    cp._client(), e.g. cp.cache_file('salt://foo').
  3. Observe KeyError: '__file_client__' / LoaderError; the __opts__
    fallback never runs.

Expected behavior

cp._client() should fall back to constructing a file client from __opts__
when __file_client__ is missing/None, instead of raising.

Suggested fix

Guard the context lookup so a missing/None context falls back cleanly:

try:
    file_client = __file_client__.value()
except salt.exceptions.LoaderError:
    file_client = None
if file_client:
    return file_client
return salt.fileclient.get_file_client(__opts__.value())

and/or have resource_modules() pack __file_client__ the same way
minion_mods() does (thread a file_client= param through the loader and
pass a shared file client from state.py).

Type of salt install

pip (git)

Major version

master / development (Salt Resources architecture; 3008)

What supported OS are you seeing the problem on?

photon-5 (architecture-independent — it is a loader/context bug, not OS-specific)

salt --versions-report output

Observed on the development (master) branch where salt.loader.resource_modules()
exists. The bug is in salt/modules/cp.py::_client() and the resource_modules
loader context packing; it is independent of platform and versions-report.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugbroken, incorrect, or confusing behaviorneeds-triage

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions