Logstash Error Guide: 'dictionary file not found' — Fix a Missing translate dictionary_path
Fix Logstash translate filter 'dictionary file not found': verify dictionary_path, file permissions, YAML/CSV format, and the refresh settings.
- #logstash
- #logging
- #troubleshooting
- #errors
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
The translate filter enriches events by mapping a source field’s value to a replacement value pulled from an external lookup table — a YAML, JSON, or CSV file referenced by dictionary_path. When Logstash loads (or reloads) the pipeline, the filter reads that file into memory. If the file does not exist, sits at a different path than configured, or the logstash user cannot read it, the plugin aborts pipeline startup and prints a configuration error.
The literal message that appears in /var/log/logstash/logstash-plain.log looks like this:
[ERROR][logstash.filters.translate] Invalid setting for translate filter plugin:
filter {
translate {
# This setting must be a path
# File does not exist or cannot be opened /etc/logstash/dictionaries/http_codes.yml
dictionary_path => "/etc/logstash/dictionaries/http_codes.yml"
...
}
}
The older / dictionary-refresh variant of the same failure reads:
[ERROR][logstash.filters.translate] Can't read dictionary:
{:path=>"/etc/logstash/dictionaries/http_codes.yml", :exception=>"No such file or directory"}
Both mean the same thing: Logstash was told to read a dictionary from disk and the read failed. Because translate validates dictionary_path at plugin registration, the entire pipeline refuses to start rather than silently skipping enrichment.
Symptoms
- Logstash fails to start, or a specific pipeline stays in a crash-restart loop, right after a config change that added or edited a
translatefilter. logstash-plain.logshowsFile does not exist or cannot be openedorCan't read dictionarynaming yourdictionary_path.bin/logstash --config.test_and_exitfails with atranslateplugin configuration error instead ofConfiguration OK.- Events flow but are never enriched — the
translatetargetfield is missing — whendictionaryrefresh silently fails after a hot reload. - The pipeline worked in one environment (dev) but not another (prod) where the dictionary file was never deployed.
Common Root Causes
- The file simply isn’t there — a typo in
dictionary_path, or the dictionary was never copied to the host during deployment (baked into a dev image but missing from the prod config-management run). - Permissions block the
logstashuser — the file exists but is owned byroot:rootwith0600, so the service account that runslogstashgetsPermission denied, which surfaces as “cannot be opened.” - Relative path resolved from the wrong working directory — a bare
dictionary_path => "dicts/codes.yml"is resolved relative to Logstash’s runtime CWD (often/usr/share/logstash), not the config directory. - Wrong file extension / format mismatch — the loader picks a parser by extension. A
.yamlor.txtfile, or a.ymlthat actually contains CSV, parses as empty or errors out. - SELinux or AppArmor denial — the file is present and world-readable but the mandatory-access-control policy forbids
logstashfrom reading outside/etc/logstash. - Broken YAML/CSV content — a tab character, unquoted colon, or ragged CSV row makes the dictionary unparseable even though the file opens fine.
- A symlink target that moved —
dictionary_pathpoints at a symlink whose destination was rotated or deleted.
Diagnostic Workflow
Start by validating the pipeline in isolation. --config.test_and_exit (aliased -t) parses and registers every plugin without ingesting data, so it reproduces the dictionary read at startup:
sudo -u logstash /usr/share/logstash/bin/logstash \
-f /etc/logstash/conf.d/enrich-http.conf \
--path.settings /etc/logstash \
--config.test_and_exit
Run it as the logstash user (sudo -u logstash) so you exercise the exact permission set the service uses — running as root can mask an EACCES you would hit in production.
Here is a representative pipeline that triggers the error. Logstash .conf files use a Ruby-like DSL, so the config reads like Ruby:
input {
beats { port => 5044 }
}
filter {
# Map an HTTP status code in [response][status] to a human label.
translate {
source => "[response][status]"
target => "[response][status_label]"
dictionary_path => "/etc/logstash/dictionaries/http_codes.yml"
fallback => "unknown"
refresh_interval => 300
exact => true
override => true
}
}
output {
elasticsearch {
hosts => ["http://localhost:9200"]
index => "http-access-%{+YYYY.MM.dd}"
}
}
Now confirm each assumption the filter makes about that file. First, does it exist and can the service account read it:
ls -l /etc/logstash/dictionaries/http_codes.yml
sudo -u logstash cat /etc/logstash/dictionaries/http_codes.yml >/dev/null && echo "readable by logstash"
If ls shows the file but the cat prints Permission denied, it is a permissions problem, not a missing file. Check ownership and the parent-directory execute bits (you need x on every directory in the path to traverse into it):
namei -l /etc/logstash/dictionaries/http_codes.yml
Validate the dictionary content itself. A YAML dictionary must be a flat map of key: value pairs:
# http_codes.yml should look like this:
# "200": "OK"
# "404": "Not Found"
# "500": "Internal Server Error"
ruby -ryaml -e 'p YAML.load_file(ARGV[0])' /etc/logstash/dictionaries/http_codes.yml
If that Ruby one-liner raises a Psych::SyntaxError, the file is present and readable but malformed — fix the YAML, don’t touch the path. Finally, rule out mandatory access control:
sudo ausearch -m avc -ts recent | grep -i logstash # SELinux denials
sudo dmesg | grep -i 'apparmor.*logstash' # AppArmor denials
Example Root Cause Analysis
A team added status-code enrichment and it passed CI, but the production pipeline entered a restart loop. logstash-plain.log showed:
[ERROR][logstash.filters.translate] Invalid setting for translate filter plugin:
# File does not exist or cannot be opened /etc/logstash/dictionaries/http_codes.yml
ls -l /etc/logstash/dictionaries/http_codes.yml returned No such file or directory, which looked like a missing-file problem. But ls -l /etc/logstash/dictionaries/ revealed the file was actually named http-codes.yml (a hyphen), while the config referenced http_codes.yml (an underscore). The dictionary was deployed correctly; the reference had a typo.
The deeper cause was that the developer authored the config on a case-and-punctuation-forgiving local setup where an old copy named http_codes.yml still lingered in the config directory, so --config.test_and_exit passed locally. The prod host, freshly provisioned by config management, only ever had the hyphenated file. The fix was one character — aligning dictionary_path to the deployed filename — plus a guard added to CI: the pipeline lint now runs test_and_exit in a container that mounts only the artifacts config management actually ships, so a stray local file can never make a broken reference pass again.
Prevention Best Practices
- Deploy dictionaries with the pipeline as one atomic unit (same package, same config-management role), so a config that references a file guarantees the file ships alongside it.
- Keep every dictionary under
/etc/logstash/dictionaries/owned byroot:logstashwith mode0640, and directories0750, so the service account can read but not write them. - Always use absolute
dictionary_pathvalues; never rely on Logstash’s runtime working directory. - Match extension to content —
.ymlfor YAML,.jsonfor JSON,.csvfor CSV — and lint the file in CI before it ever reaches a host. - Run
--config.test_and_exitas thelogstashuser in CI so permission and path problems fail the build, not production. - Set a sensible
fallbackso a lookup miss produces a known value instead of an unmapped event, and pinrefresh_intervalso edits reload without a restart.
Quick Command Reference
# Validate the full pipeline as the service account (reproduces the dictionary read)
sudo -u logstash /usr/share/logstash/bin/logstash \
-f /etc/logstash/conf.d/enrich-http.conf \
--path.settings /etc/logstash --config.test_and_exit
# Confirm existence + readability by the logstash user
ls -l /etc/logstash/dictionaries/http_codes.yml
sudo -u logstash test -r /etc/logstash/dictionaries/http_codes.yml && echo readable
# Show every permission bit along the path
namei -l /etc/logstash/dictionaries/http_codes.yml
# Parse-check the dictionary content
ruby -ryaml -e 'p YAML.load_file(ARGV[0])' /etc/logstash/dictionaries/http_codes.yml
# Fix ownership / permissions
sudo chown root:logstash /etc/logstash/dictionaries/http_codes.yml
sudo chmod 0640 /etc/logstash/dictionaries/http_codes.yml
# Watch the log while restarting the service
sudo systemctl restart logstash
sudo tail -f /var/log/logstash/logstash-plain.log
# Check for MAC denials
sudo ausearch -m avc -ts recent | grep -i logstash
Conclusion
dictionary file not found and Can't read dictionary are startup-blocking errors from the translate filter, and they almost always come down to one of three things: the file isn’t where dictionary_path says it is, the logstash user can’t read it, or its contents don’t parse. Reproduce the read with --config.test_and_exit run as the service account, then walk the path with ls, namei -l, and a quick parser check to see exactly which assumption broke. Ship dictionaries atomically with their pipelines, keep them absolute-pathed and correctly owned, and lint them in CI, and this class of error stops reaching production entirely.
Get 500 Battle-Tested DevOps AI Prompts — Free
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.