Skip to content

[Bug]: SyncClientMixin.low() masks all early failures with UnboundLocalError: proc_fn #70252

Description

@xescab

What happened?

In salt/client/mixins.py::SyncClientMixin.low(), proc_fn is assigned at line 381, but the finally block at line 415 unconditionally references it at line 418:

Anything raising between lines 339 and 380 is correctly captured by the broad except at 404 — and then discarded, because the finally raises UnboundLocalError on its way out. Affected call sites include verify_fun() (line 340), the func_globals injection loop, salt.utils.args.format_call(), and jid_event.fire_event(data, "new").

Two consequences:

The real exception is never logged or returned. Callers see only UnboundLocalError: cannot access local variable 'proc_fn' where it is not associated with a value, which points at cleanup code unrelated to the actual fault.
Because the finally raises, execution never reaches the store_job() call or namespaced_event.fire_event(data, "ret") that follow it, so the failure is absent from the job cache and the event bus too.
Note that a permissions failure on the proc file itself does not trigger this — proc_fn is already bound by the time fopen() runs at line 382. The UnboundLocalError is always a symptom of an earlier, hidden fault, which makes it actively misleading to diagnose.

Any master. Reproducible with a plain RunnerClient; no special configuration required.

Steps to reproduce

import salt.config
import salt.runner

opts = salt.config.master_config("/etc/salt/master")
client = salt.runner.RunnerClient(opts)
client.low("nonexistent.function", {"fun": "nonexistent.function"})

The control flow in isolation:

import os

def low(fail_early):
try:
if fail_early:
raise RuntimeError("the real error")
proc_fn = os.path.join("/tmp", "proc", "jid")
open(proc_fn, "w+b")
except (Exception, SystemExit) as ex:
print("broad except captured:", ex) # real error captured here...
finally:
try:
os.remove(proc_fn) # ...then destroyed here
except OSError:
pass

low(True)

broad except captured: the real error

UnboundLocalError: local variable 'proc_fn' referenced before assignment

Expected behavior

verify_fun() raises CommandExecutionError: 'nonexistent.function' is not available., and low() returns it in data["return"] as the broad except intends.

Actual behavior

UnboundLocalError: cannot access local variable 'proc_fn' where it is not associated with a value

Suggested fix

         try:
  •            proc_fn = None
               self_functions = copy.copy(self.functions)
               salt.utils.lazy.verify_fun(self_functions, fun)
    

@@
finally:
# Job has finished or issue found, so let's clean up after ourselves

  •            try:
    
  •                os.remove(proc_fn)
    
  •            except OSError as err:
    
  •                log.debug("Error attempting to remove master job tracker: %s", err)
    
  •            if proc_fn is not None:
    
  •                try:
    
  •                    os.remove(proc_fn)
    
  •                except OSError as err:
    
  •                    log.debug("Error attempting to remove master job tracker: %s", err)
    

Type of salt install

Official pkg

Major version

3008.x

What supported OS are you seeing the problem on? Can select multiple. (If bug appears on an unsupported OS, please open a GitHub Discussion instead)

ubuntu-22.04

salt --versions-report output

Salt Version:
            Salt: 3008.2

Python Version:
          Python: 3.14.6 (main, Jun 11 2026, 02:19:05) [GCC 11.2.0]

Dependency Versions:
            cffi: 2.0.0
        cherrypy: 18.10.0
    cryptography: 48.0.0
        dateutil: 2.9.0.post0
       docker-py: Not Installed
           gitdb: 4.0.12
       gitpython: 3.1.50
          Jinja2: 3.1.6
         libgit2: Not Installed
    looseversion: 1.3.0
        M2Crypto: Not Installed
            Mako: Not Installed
         msgpack: 1.1.2
    msgpack-pure: Not Installed
    mysql-python: Not Installed
       packaging: 24.0
       pycparser: 3.00
        pycrypto: 3.23.0
    pycryptodome: 3.23.0
          pygit2: Not Installed
    python-gnupg: 0.5.6
          PyYAML: 6.0.3
           PyZMQ: 27.1.0
          relenv: 0.22.14
           smmap: 5.0.2
         timelib: 0.3.0
         Tornado: 6.5.7
             ZMQ: 4.3.5

Salt Extensions:
 saltext.azurerm: 4.4.1

Salt Package Information:
    Package Type: onedir

System Versions:
            dist: ubuntu 22.04.5 jammy
          locale: utf-8
         machine: x86_64
         release: 6.8.0-1053-aws
          system: Linux
         version: Ubuntu 22.04.5 jammy

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugbroken, incorrect, or confusing behaviorneeds-triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions