{"record":{"id":"468b44e16395e7ac","repo":"basecamp/kamal","slug":"secret-key-not-found-in-secrets-files-join","errorCode":null,"errorMessage":"Secret '#{key}' not found in #{secrets_files.join(\", \")}","messagePattern":"Secret '#(.+?)' not found in #(.+?)","errorType":"exception","errorClass":"Kamal::ConfigurationError","httpStatus":null,"severity":"error","filePath":"lib/kamal/secrets.rb","lineNumber":16,"sourceCode":"require \"dotenv\"\n\nclass Kamal::Secrets\n  Kamal::Secrets::Dotenv::InlineCommandSubstitution.install!\n\n  def initialize(destination: nil, secrets_path: \".kamal/secrets\")\n    @destination = destination\n    @secrets_path = secrets_path\n    @mutex = Mutex.new\n  end\n\n  def [](key)\n    synchronized_fetch(key)\n  rescue KeyError\n    if secrets_files.present?\n      raise Kamal::ConfigurationError, \"Secret '#{key}' not found in #{secrets_files.join(\", \")}\"\n    else\n      raise Kamal::ConfigurationError, \"Secret '#{key}' not found, no secret files (#{secrets_filenames.join(\", \")}) provided\"\n    end\n  end\n\n  def to_h\n    secrets\n  end\n\n  def secrets_files\n    @secrets_files ||= secrets_filenames.select { |f| File.exist?(f) }\n  end\n\n  def key?(key)\n    synchronized_fetch(key).present?\n  rescue KeyError\n    false\n  end","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/secrets.rb#L1-L34","documentation":"Kamal resolves ERb like <%= secrets.PASSWORD %> in deploy.yml through Kamal::Secrets, which Dotenv-parses .kamal/secrets-common and .kamal/secrets (or .kamal/secrets.<destination> when a destination is set). This Kamal::ConfigurationError means at least one secrets file exists, but none of them defines the requested key, so the lookup (secrets.fetch) raised KeyError and was converted.","triggerScenarios":"deploy.yml references secrets.PASSWORD but no line PASSWORD=... exists in .kamal/secrets-common or the destination-specific file; the key was fetched under a different name; the key exists only in a different destination's file (only common + current destination are read).","commonSituations":"Adding a new env/secret reference to deploy.yml before running kamal secrets fetch; typo between deploy.yml and the secrets file; fetching secrets while a destination (-d staging) is active so they land in .kamal/secrets.staging but deploying without the destination.","solutions":["Fetch or append the missing key: kamal secrets fetch -a <adapter> --account <acct> PASSWORD >> .kamal/secrets","Or add it manually as KEY=value in .kamal/secrets (same format as dotenv)","Verify the exact key names with kamal secrets print and compare against every <%= secrets.X %> reference in deploy.yml","If using destinations, confirm the key lives in .kamal/secrets-common or the .kamal/secrets.<destination> you deploy with"],"exampleFix":"# error: Secret 'PASSWORD' not found in .kamal/secrets-common, .kamal/secrets\n\n# fix (terminal)\nkamal secrets fetch -a bitwarden-sm PASSWORD >> .kamal/secrets\n\n# or (.kamal/secrets)\n# before: (no PASSWORD line)\n# after:\nPASSWORD=correct-horse-battery-staple","handlingStrategy":"validation","validationCode":"# Verify every <%= secrets.X %> reference in deploy.yml resolves before deploying\nrequire \"yaml\"\n\ndeploy = File.read(\"config/deploy.yml\")\nreferenced = deploy.scan(/secrets\\.([A-Za-z0-9_]+)/).flatten.uniq\n\nfiles = [\".kamal/secrets-common\", \".kamal/secrets\", \".kamal/secrets.#{ENV[\"KAMAL_DESTINATION\"]}\"].compact\nexisting = files.select { |f| File.exist?(f) }\nexisting.each do |f|\n  referenced -= File.readlines(f).map { |l| l[/\\A([A-Za-z0-9_]+)=/, 1] }.compact\nend\n\nabort \"unresolved secrets: #{referenced.join(\", \")}\" unless referenced.empty?","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run `kamal secrets print` after every fetch to confirm key names match deploy.yml exactly","Add the referenced-vs-defined check above as a CI step so a new <%= secrets.X %> without a fetch fails the build","Keep shared keys in .kamal/secrets-common and destination-only keys in .kamal/secrets.<destination>"],"tags":["kamal","secrets","configuration","dotenv","deployment"],"backgroundTag":"missing-env-var","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}