Monitor: "Failed to load triggers" when a secret source is spelled env instead of environment
Written 5 October 2026
Applies to OpenZeppelin Monitor 1.6.0 (and, from the source, any version with the SecretValue type).
Symptom
The Monitor exits at startup with:
Failed to load triggers [path=default]
Your trigger files parse as JSON, the referenced environment variables are set, and the same secret block works fine in a Relayer config.json.
Cause
Monitor and Relayer spell the environment-variable secret source differently.
- Monitor (
src/): the acceptedmodels/ security/ secret. rs typevalues areplain,environmentandhashicorpcloudvault, matched case-insensitively.envis not one of them, so the trigger file fails to deserialize and the repository reports the generic "Failed to load triggers". - Relayer: the same idea is spelled
{"type": "env", "value": "VAR"}.
If you copied a secret block from a Relayer example or wrote both configurations in one sitting, this is the mismatch.
Fix
Before:
"slack_url": {"type": "env", "value": "SLACK_WEBHOOK_URL"}
After:
"slack_url": {"type": "environment", "value": "SLACK_WEBHOOK_URL"}
Then validate without starting the service:
./openzeppelin-monitor --check
(or run the container with --check appended to the command). It parses every file under config/ and verifies that monitors, networks and triggers reference each other correctly.
In 1.6.0 --check exits with status 0 even when validation fails (openzeppelin-monitor #501), so a script has to read its output: success prints Configuration validation completed successfully!, and a failure prints the error without that line.
./openzeppelin-monitor --check 2>&1 | tee check.log
grep -q 'Configuration validation completed successfully!' check.log
Other causes of the same line
The message wraps any failure while reading and deserializing config/ (src/, load_all), so a malformed file, a missing required field, or a trigger_type outside slack, discord, telegram, email, webhook and script produce it too. A monitor that references a trigger id that does not exist is reported separately, during monitor validation. Remember there is no hot reload: after fixing the file, restart the Monitor.