{"record":{"id":"76a1e647735b4534","repo":"basecamp/kamal","slug":"secret-key-not-found-no-secret-files-secr","errorCode":null,"errorMessage":"Secret '#{key}' not found, no secret files (#{secrets_filenames.join(\", \")}) provided","messagePattern":"Secret '#(.+?)' not found, no secret files \\(#(.+?)\\) provided","errorType":"exception","errorClass":"Kamal::ConfigurationError","httpStatus":null,"severity":"error","filePath":"lib/kamal/secrets.rb","lineNumber":18,"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\n\n  private","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/secrets.rb#L1-L36","documentation":"Variant of the missing-secret error where zero secrets files were found on disk: neither .kamal/secrets-common nor .kamal/secrets/.kamal/secrets.<destination> exists (see secrets_filenames). The message lists the exact filenames Kamal looked for, so no secret reference in deploy.yml can ever be satisfied.","triggerScenarios":"deploy.yml uses <%= secrets.X %> on a fresh clone where .kamal/secrets* were never created (they are typically gitignored); running kamal from a directory other than the app root; a destination is active (KAMAL_DESTINATION or -d) so Kamal looks for .kamal/secrets.staging instead of .kamal/secrets.","commonSituations":"First deploy from a new machine or CI checkout; forgetting -d so the wrong destination file is searched; running kamal inside a subdirectory; onboarding a teammate without documenting the secrets bootstrap step.","solutions":["Create the secrets file(s) by fetching: kamal secrets fetch -a <adapter> --account <acct> KEY_A KEY_B >> .kamal/secrets","If using destinations, rerun with the right destination: kamal -d staging secrets fetch ... >> .kamal/secrets.staging (or put shared keys in .kamal/secrets-common)","Run kamal from the project root (where .kamal/ lives)","Bootstrap CI by storing the secrets file as a secure CI variable or fetching it from a vault in a pipeline step"],"exampleFix":"# error: Secret 'PASSWORD' not found, no secret files (.kamal/secrets-common, .kamal/secrets) provided\n\n# fix (terminal)\nkamal secrets fetch -a one_password --account me@example.com RAILS_MASTER_KEY PASSWORD >> .kamal/secrets\nkamal secrets print   # verify keys resolve","handlingStrategy":"validation","validationCode":"names = [\".kamal/secrets-common\", \".kamal/secrets#{ENV[\"KAMAL_DESTINATION\"] && \".#{ENV[\"KAMAL_DESTINATION\"]}\"}]\nabort \"no secrets files found (looked for #{names.join(\", \")}) — run kamal secrets fetch first\" unless names.any? { |f| File.exist?(f) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Bootstrap new machines/CI with a documented `kamal secrets fetch ... >> .kamal/secrets` step","Always run kamal from the project root so relative .kamal/secrets* paths resolve","Double-check -d <destination> — it changes which secrets file name Kamal searches for"],"tags":["kamal","secrets","configuration","dotenv","bootstrap"],"backgroundTag":"missing-env-var","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}