IaC Error Guide: 'No matching sls found' SaltStack State Apply Failure
Fix SaltStack 'No matching sls found' and 'Minion did not return' on state.apply: repair file_roots, top.sls and environment mismatches, unaccepted keys, and gitfs sync.
- #iac
- #troubleshooting
- #errors
- #saltstack
Overview
When you run a Salt state, the master compiles the requested SLS files from its file_roots for the targeted environment, ships the rendered state to the minion, and the minion executes it. Two failures dominate this path: the master cannot locate the SLS you asked for (No matching sls found), or the minion never reports back (Minion did not return). Both stop the run before any state is enforced, leaving the target unconfigured.
You will see the lookup failure from salt 'web01' state.apply nginx:
web01:
Data failed to compile:
----------
No matching sls found for 'nginx' in env 'base'
ERROR: Minions returned with non-zero exit code
Or the no-return failure:
web01:
Minion did not return. [No response]
The minions may not have all finished running and any remaining minions will return upon completion. To look up the return data for this job later, run the following command:
salt-run jobs.lookup_jid 20260623140811445566
No matching sls found is a master-side path/environment problem — the file simply is not where Salt looks. Minion did not return is a connectivity or timeout problem — the minion is down, its key is not accepted, or the run outlasted the timeout. The two are diagnosed differently, so identify which you have first.
Symptoms
state.apply <name>ends withNo matching sls found for '<name>' in env '<env>'.state.apply(highstate) ignores a minion becausetop.slsdoes not match it.- A target returns
Minion did not return. [No response]. salt 'minion' test.pingreturns nothing orMinion did not return.
salt 'web01' state.apply nginx
web01:
Data failed to compile:
----------
No matching sls found for 'nginx' in env 'base'
salt-run fileserver.file_list | grep nginx
(Empty — the master’s fileserver does not expose any nginx SLS, confirming a path/roots problem.)
Common Root Causes
1. SLS path or top.sls name mismatch
state.apply nginx resolves to nginx.sls or nginx/init.sls under file_roots. A typo, a wrong subdirectory, or a top.sls that references a non-existent state produces the error.
salt-run fileserver.file_list | grep -E 'nginx'
ls -R /srv/salt/nginx 2>/dev/null
nginx/server.sls
(The file is nginx/server.sls, but there is no nginx.sls or nginx/init.sls, so state.apply nginx finds nothing — you must call state.apply nginx.server.)
2. file_roots misconfigured on the master
The master’s file_roots does not include the directory holding your states, so the fileserver never exposes them.
grep -A6 '^file_roots' /etc/salt/master /etc/salt/master.d/*.conf 2>/dev/null
file_roots:
base:
- /srv/salt-old
(States live under /srv/salt, but file_roots points at /srv/salt-old — nothing matches.)
3. Minion not connected or key not accepted
If the minion’s key is unaccepted (or rejected), the master will not target it, and runs return no data.
salt-key -L
Accepted Keys:
db01
Denied Keys:
Unaccepted Keys:
web01
Rejected Keys:
(web01 is unaccepted — accept it with salt-key -a web01 before it can return.)
4. Minion down or run timed out
The minion service is stopped, unreachable, or the state took longer than the master’s timeout, yielding Minion did not return.
salt 'web01' test.ping
web01:
Minion did not return. [No response]
(Check the minion directly: systemctl status salt-minion on the host, or raise -t 60 on the run.)
5. Environment (base/prod) mismatch
The state exists only in prod, but you applied it against base (the default). The lookup is per-environment, so the file is invisible from the wrong env.
salt-run fileserver.envs
salt 'web01' state.show_top
- base
- prod
web01:
----------
prod:
- nginx
(The minion’s top matches under prod; running with saltenv=base finds no nginx.)
6. gitfs not synced or a Jinja render error
When states come from gitfs, an unsynced remote means the SLS is absent until you refresh. A Jinja templating error inside the SLS also surfaces during compile, before the state runs.
salt-run fileserver.update
salt 'web01' state.apply nginx.server
web01:
Data failed to compile:
----------
Rendering SLS 'base:nginx.server' failed: Jinja variable 'pillar' is undefined
(A gitfs refresh fixes a missing file; a Jinja error like an undefined pillar value must be fixed in the template.)
Diagnostic Workflow
Step 1: Confirm which failure you have
salt 'web01' state.apply nginx
No matching sls found is a master-side path/env problem (causes 1, 2, 5, 6). Minion did not return is connectivity/timeout (causes 3, 4).
Step 2: Verify the minion is reachable and accepted
salt-key -L
salt 'web01' test.ping
If the key is unaccepted, accept it. If test.ping returns no response, the minion is down or unreachable — fix that before anything else.
Step 3: Ask the fileserver what it actually exposes
salt-run fileserver.file_list | grep -i nginx
salt-run fileserver.envs
If your SLS is absent from the list, the master cannot serve it — a file_roots, path, or unsynced gitfs problem.
Step 4: Check the resolved top and environment
salt 'web01' state.show_top
grep -A8 '^file_roots' /etc/salt/master
Compare the env where the state lives to the env you ran against. Re-run with the right saltenv= if they differ.
Step 5: Sync, then re-apply (and bypass the master to isolate)
salt-run fileserver.update # refresh gitfs
salt 'web01' saltenv=prod state.apply nginx.server
# Isolate master vs. minion by running locally on the host:
salt-call --local state.apply nginx.server -l debug
salt-call --local runs the state from the minion’s own cached files, which separates a master-side lookup problem from a minion-side execution problem.
Example Root Cause Analysis
A new state monitoring was added to a gitfs-backed repo and pushed, but applying it fails on every minion:
web01:
Data failed to compile:
----------
No matching sls found for 'monitoring' in env 'base'
The minion is clearly reachable (other states apply), so this is a master-side lookup. Asking the fileserver what it serves:
salt-run fileserver.file_list | grep monitoring
The new SLS is missing even though it was pushed — the master’s gitfs cache predates the push. Forcing a refresh:
salt-run fileserver.update
salt-run fileserver.file_list | grep monitoring
monitoring/init.sls
After the gitfs cache picks up the new commit, the file appears and the state applies cleanly:
salt 'web01' state.apply monitoring
web01:
Summary for web01
Succeeded: 7
Failed: 0
Cause 6 — gitfs was simply stale; the fileserver.update made the new SLS visible.
Prevention Best Practices
- Keep
file_roots(or gitfs remotes) and your repository layout in lockstep, and document the env-to-branch mapping sosaltenvis never ambiguous. - Enforce a refresh step (
salt-run fileserver.update) in your deploy pipeline after every push to gitfs, so the master never serves stale states. - Automate key acceptance for trusted minions (autosign with constraints, or a reviewed
salt-key -astep) so new hosts are not silently skipped. - Validate SLS rendering in CI with
salt-call --local state.show_sls <name>against a representative minion to catch Jinja errors before they hit production. - Treat your Salt tree as infrastructure as code: review
top.slsand environment changes the same way you review application code. - When a highstate fails mid-rollout, the free incident assistant can separate a master-side
No matching sls foundfrom a minion-sidedid not returnand point at the next command.
Quick Command Reference
# Reproduce the failure
salt 'web01' state.apply nginx
# Minion connectivity and key state
salt-key -L
salt 'web01' test.ping
# What the master fileserver actually exposes
salt-run fileserver.file_list | grep -i nginx
salt-run fileserver.envs
# Resolved top and file_roots
salt 'web01' state.show_top
grep -A8 '^file_roots' /etc/salt/master
# Refresh gitfs, then re-apply in the right env
salt-run fileserver.update
salt 'web01' saltenv=prod state.apply nginx.server
# Isolate master vs minion by running locally
salt-call --local state.apply nginx.server -l debug
# Recover return data for a no-return job
salt-run jobs.lookup_jid <JID>
Conclusion
No matching sls found and Minion did not return are the two ways a state.apply fails before enforcing anything — one master-side, one minion-side. The usual root causes:
- An SLS path or
top.slsname that does not resolve to<name>.sls/<name>/init.sls. file_rootson the master pointing somewhere other than your states.- A minion whose key is unaccepted, so the master never targets it.
- A minion that is down or a run that outlived the timeout.
- An environment mismatch — the state lives in
prodbut you ran againstbase. - A stale gitfs cache, or a Jinja render error inside the SLS.
Decide which failure you have first, confirm the minion is reachable and accepted, then ask the fileserver what it actually serves — most No matching sls found errors are a path, env, or unsynced-gitfs problem you can see in one command.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.