agents/docs/troubleshooting.md
Common gotchas and solutions when developing Salt.
Salt enforces strict import order via isort (profile 3, trailing commas). Configuration is in pyproject.toml.
# 1. Standard library imports
import logging
import os
import sys
# 2. Salt imports
import salt.exceptions
import salt.utils.args
import salt.utils.platform
# 3. Third-party imports
import requests
# Auto-fix import order
isort .
# Check without modifying
isort --check .
Module directories need __init__.py files for Python to recognize them as packages.
# Check for missing __init__.py
find salt/modules -type d ! -exec test -e {}/__init__.py \; -print
The filename determines the module namespace:
salt/modules/pkg.py → accessible as pkg.*__virtualname__ to override the nameFilename should match virtualname when possible to avoid loader overhead.
Good:
# File: salt/modules/systemd_service.py
__virtualname__ = "service" # Will be service.* on systemd systems
Less efficient:
# File: salt/modules/my_totally_different_name.py
__virtualname__ = "service" # Loader has to scan more files
Changes to salt/loader/ affect all plugin types (modules, states, grains, pillars, etc.).
# Run loader unit tests
nox -e test-3 -- tests/pytests/unit/test_loader.py
# Run integration tests
nox -e test-3 -- tests/pytests/integration/
__context__ properly on reload__virtual__() is called correctlyIf tests hang indefinitely:
# Kill stale Salt processes
pkill -9 -f salt
# Check for port conflicts
lsof -i :4505 # Publisher port
lsof -i :4506 # Request port
python -c "import zmq; print(zmq.zmq_version())"
If this fails, reinstall ZeroMQ:
pip uninstall pyzmq
pip install pyzmq
Salt uses:
If these ports are in use, tests will fail or hang.
When running tests in containers:
Changes to salt/ source code are visible immediately (mounted volume):
# Edit on host
vim salt/modules/pkg.py
# Immediately visible in container
docker exec mycontainer cat /salt/salt/modules/pkg.py
Changes to installed Salt library may need manual sync:
# After editing source, copy to installed location
docker exec <NAME> cp /salt/salt/modules/foo.py \
/salt/artifacts/salt/lib/python3.11/site-packages/salt/modules/
/root/.local/relenv/toolchain/, but relenv uses /root/.cache/relenv/toolchains/Use ULA IPv6 addresses with NAT:
# In container config
networks:
default:
enable_ipv6: true
ipam:
config:
- subnet: fd00:db8::/64
Create symlink in container:
docker exec <NAME> bash -c "mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain"
This is required for all Python 3.11+ container tests.
Update test mocks to handle conditional import:
# Old (fails on 3.11+)
import backports.ssl
# New (handles both)
try:
import backports.ssl
except ImportError:
import ssl as backports_ssl
Tests fail with confusing errors like "module not found" or "wrong version".
Always clean before downloading new CI artifacts:
rm -rf artifacts/ nox-*.zip nox.*.tar.*
Artifacts are cached locally. If you download artifacts from a different run, old files may conflict.
Modules only load on first access. Import-time side effects may not occur until first use of a module function.
# Module import doesn't execute __virtual__()
import salt.modules.systemd_service
# __virtual__() executes on first function call
salt.modules.systemd_service.restart("nginx")
__virtual__() failures only visible on first useIf pre-commit hooks don't run on commit:
# Ensure you're in venv311
source venv311/bin/activate
# Install hooks
pre-commit install
# Test manually
pre-commit run --all-files
If hooks fail:
# Run specific hook
pre-commit run black --all-files
pre-commit run isort --all-files
# Auto-fix issues
black .
isort .
Only skip hooks if absolutely necessary:
git commit --no-verify
If you get ImportError: cannot import name 'SaltInvocationError':
# Wrong
from salt.exceptions import SaltInvocationError
# Check spelling and case
from salt.exceptions import (
CommandExecutionError,
SaltInvocationError, # Note: capital I
)
Salt's loader system can sometimes create circular imports. Solutions:
__salt__ instead of direct importsalt/utils/This often indicates:
Solution: Use proper teardown and isolation.
Common issues:
# Wrong - mocking after import
import salt.modules.mymodule as mymodule
with patch("some.function"): # Too late!
mymodule.my_function()
# Right - mock before use
with patch("salt.modules.mymodule.some_function"):
import salt.modules.mymodule as mymodule
mymodule.my_function()
# Better - use patch.dict for dunders
with patch.dict(mymodule.__salt__, {"cmd.run": MagicMock()}):
mymodule.my_function()
If tests are slow:
Keep __virtual__() fast:
# Bad - expensive operation
def __virtual__():
packages = fetch_all_packages() # Slow!
if "mypackage" in packages:
return True
return False
# Good - quick check
def __virtual__():
if salt.utils.path.which("mycommand"):
return True
return False
pkill -9 -f saltpython -c "import zmq"lsof -i :4505 :4506__virtual__() return valueIf you're still stuck:
doc/ directoryCONTRIBUTING.rst