{"record":{"id":"1f64f7415e2b927e","repo":"basecamp/kamal","slug":"could-not-read-secrets-from-bitwarden-secrets-mana","errorCode":null,"errorMessage":"Could not read secrets from Bitwarden Secrets Manager","messagePattern":"Could not read secrets from Bitwarden Secrets Manager","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb","lineNumber":28,"sourceCode":"    GET_COMMAND = \"secret get\"\n\n    def fetch_secrets(secrets, from:, account:, session:)\n      raise RuntimeError, \"You must specify what to retrieve from Bitwarden Secrets Manager\" if secrets.length == 0\n\n      secrets = prefixed_secrets(secrets, from: from)\n      command, project = extract_command_and_project(secrets)\n\n      {}.tap do |results|\n        if command.nil?\n          secrets.each do |secret_uuid|\n            item_json = run_command(\"#{GET_COMMAND} #{secret_uuid.shellescape}\")\n            raise RuntimeError, \"Could not read #{secret_uuid} from Bitwarden Secrets Manager\" unless $?.success?\n            item_json = JSON.parse(item_json)\n            results[item_json[\"key\"]] = item_json[\"value\"]\n          end\n        else\n          items_json = run_command(command)\n          raise RuntimeError, \"Could not read secrets from Bitwarden Secrets Manager\" unless $?.success?\n\n          JSON.parse(items_json).each do |item_json|\n            results[item_json[\"key\"]] = item_json[\"value\"]\n          end\n        end\n      end\n    end\n\n    def extract_command_and_project(secrets)\n      if secrets.length == 1\n        if secrets[0] == LIST_ALL_SELECTOR\n          [ LIST_COMMAND, nil ]\n        elsif secrets[0].end_with?(LIST_ALL_FROM_PROJECT_SUFFIX)\n          project = secrets[0].split(LIST_ALL_FROM_PROJECT_SUFFIX).first\n          [ \"#{LIST_COMMAND} #{project.shellescape}\", project ]\n        end\n      end\n    end","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb#L10-L46","documentation":"In list mode (selector `all` or `<project>/all`) the adapter runs `bws secret list` (optionally with a project id); a non-zero exit raises RuntimeError \"Could not read secrets from Bitwarden Secrets Manager\". This is a bulk-fetch failure: authentication (BWS_ACCESS_TOKEN) was checked earlier via `bws project list`, so the usual causes are project-level access or an invalid project id.","triggerScenarios":"kamal secrets fetch -a bitwarden-sm all with a token whose machine account has no projects/secrets assigned; `<project-id>/all` where the id is wrong or the machine account lacks access to that project; bws CLI version mismatch; transient API/network failure.","commonSituations":"Machine account created without project assignments; copying a project id from a different organization; token rotated to an account with narrower scope; network issues on CI runners.","solutions":["Run bws project list to confirm which project ids the token can actually see","Use a project id from that list in the selector: kamal secrets fetch -a bitwarden-sm <project-id>/all","In the Bitwarden Secrets Manager web vault, grant the machine account access to the project containing the secrets","Retry once for transient network errors; upgrade the bws CLI if project list works but secret list errors"],"exampleFix":"# before\nkamal secrets fetch -a bitwarden-sm 3f9a2b1c-xxxx/all\n# => RuntimeError: Could not read secrets from Bitwarden Secrets Manager\n\n# fix (terminal)\nbws project list                       # copy a project id you can access\nkamal secrets fetch -a bitwarden-sm 8d2e4f6a-yyyy/all","handlingStrategy":"try-catch","validationCode":"abort \"BWS_ACCESS_TOKEN not set or invalid\" unless system(\"bws project list > /dev/null 2>&1\")\nprojects = JSON.parse(`bws project list`).map { |p| p[\"id\"] }\nabort \"project #{project_id} not accessible; available: #{projects.join(\", \")}\" unless projects.include?(project_id)","typeGuard":"def bws_project_accessible?(project_id)\n  JSON.parse(`bws project list`).any? { |p| p[\"id\"] == project_id }\nend","tryCatchPattern":"begin\n  adapter.fetch([\"#{project_id}/all\"])\nrescue RuntimeError => e\n  if e.message.include?(\"Could not read secrets from Bitwarden Secrets Manager\")\n    warn \"run `bws project list` — the machine account may lack access to #{project_id}\"\n  end\n  raise\nend","preventionTips":["Always take project ids from `bws project list` output for the token in use","Assign the machine account to the project in the Bitwarden SM web vault before first fetch","Include a `bws project list` health check at the start of CI pipelines that bulk-fetch secrets"],"tags":["kamal","secrets","bitwarden","authentication","access-token"],"backgroundTag":"vault-item-not-found","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}