basecamp/kamal · error · RuntimeError
Could not read #{secrets} from Doppler
Error message
Could not read #{secrets} from Doppler What it means
Raised in Kamal::Secrets::Adapters::Doppler#fetch_secrets when the backtick command `doppler secrets get <names> --json <flags>` exits non-zero. Login already succeeded (or was skipped via service token); this failure is about reading the requested secret names from the resolved Doppler project/config. The message interpolates the full requested list, including any `from` prefix.
Source
Thrown at lib/kamal/secrets/adapters/doppler.rb:26
unless loggedin?
`doppler login -y`
raise RuntimeError, "Failed to login to Doppler" unless $?.success?
end
end
def loggedin?
`doppler me --json 2> /dev/null`
$?.success?
end
def fetch_secrets(secrets, from:, **)
secrets = prefixed_secrets(secrets, from: from)
flags = secrets_get_flags(secrets)
secret_names = secrets.collect { |s| s.split("/").last }
items = `doppler secrets get #{secret_names.map(&:shellescape).join(" ")} --json #{flags}`
raise RuntimeError, "Could not read #{secrets} from Doppler" unless $?.success?
items = JSON.parse(items)
items.transform_values { |value| value["computed"] }
end
def secrets_get_flags(secrets)
unless service_token_set?
project, config, _ = secrets.first.split("/")
unless project && config
raise RuntimeError, "Missing project or config from '--from=project/config' option"
end
project_and_config_flags = "-p #{project.shellescape} -c #{config.shellescape}"
end
end
View on GitHub (pinned to eee0083b38)
Solutions
- Run the same command the adapter builds to see the real CLI error: `doppler secrets get <NAME> -p <project> -c <config> --json`.
- Verify every name in your kamal secrets list exists in that project/config (`doppler secrets list -p X -c Y`) and fix typos/renames in deploy.yml.
- Confirm --from matches the actual Doppler project/config slugs (not names) shown in the dashboard.
- If using DOPPLER_TOKEN, check the service token's project/config scope and workplace access.
Example fix
# before: deploy.yml requests a secret Doppler does not have # secrets: # - RAILS_MASTER_KEY # absent in doppler project/blog/prod # after: add it in Doppler (or remove from deploy.yml) # doppler secrets set RAILS_MASTER_KEY=... -p blog -c prod kamal secrets pull
Defensive patterns
Strategy: try-catch
Validate before calling
require "open3"
def doppler_secrets_exist?(names, project:, config:)
out, _err, status = Open3.capture3("doppler", "secrets", "get", *names, "-p", project, "-c", config, "--json")
return false unless status.success?
JSON.parse(out).keys.slice_when { true }.to_a.flatten # available names
JSON.parse(out).key?(names.first)
rescue Errno::ENOENT
false
end Try / catch
begin
secrets = adapter.fetch(%w[RAILS_MASTER_KEY], from: "blog/prod")
rescue RuntimeError => e
if e.message.include?("Could not read") && e.message.include?("from Doppler")
raise "Doppler fetch failed: verify each name exists in project/config — run `doppler secrets list -p blog -c prod`"
end
raise
end Prevention
- Keep deploy.yml's secret list and Doppler's project/config in sync via a lint step that cross-checks `doppler secrets list`.
- Prefer a service token scoped to exactly the deploy project/config so wrong-project reads fail loudly and early.
- Print the adapter's exact CLI command in debug logs to speed up diagnosis.
When it happens
Trigger: adapter.fetch(...) after successful auth when: one or more secret names do not exist in the project/config (doppler secrets get fails on unknown names); -p/-c flags derived from `--from=project/config` point at a nonexistent project or config; the service token lacks read scope for the project; Doppler API unreachable (network/proxy); or a mixed secrets list where the first entry has project/config but later entries resolve against a different config.
Common situations: Secret renamed or deleted in Doppler but still listed in deploy.yml secrets; --from default in deploy.yml pointing at an old project slug; personal token works locally but CI service token is scoped to another workplace; corporate proxy blocking api.doppler.com.
Related errors
- Failed to login to Doppler
- Could not read #{secrets} from LastPass
- Could not authenticate to Bitwarden Secrets Manager. Did you
- Missing project or config from '--from=project/config' optio
- Doppler CLI is not installed
AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21).
Data as JSON: /api/errors/a3dc95e1cbb76323.
Report an issue: GitHub.