basecamp/kamal · error · RuntimeError

Could not read secrets from Bitwarden Secrets Manager

Error message

Could not read secrets from Bitwarden Secrets Manager

What it means

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.

Source

Thrown at lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb:28

    GET_COMMAND = "secret get"

    def fetch_secrets(secrets, from:, account:, session:)
      raise RuntimeError, "You must specify what to retrieve from Bitwarden Secrets Manager" if secrets.length == 0

      secrets = prefixed_secrets(secrets, from: from)
      command, project = extract_command_and_project(secrets)

      {}.tap do |results|
        if command.nil?
          secrets.each do |secret_uuid|
            item_json = run_command("#{GET_COMMAND} #{secret_uuid.shellescape}")
            raise RuntimeError, "Could not read #{secret_uuid} from Bitwarden Secrets Manager" unless $?.success?
            item_json = JSON.parse(item_json)
            results[item_json["key"]] = item_json["value"]
          end
        else
          items_json = run_command(command)
          raise RuntimeError, "Could not read secrets from Bitwarden Secrets Manager" unless $?.success?

          JSON.parse(items_json).each do |item_json|
            results[item_json["key"]] = item_json["value"]
          end
        end
      end
    end

    def extract_command_and_project(secrets)
      if secrets.length == 1
        if secrets[0] == LIST_ALL_SELECTOR
          [ LIST_COMMAND, nil ]
        elsif secrets[0].end_with?(LIST_ALL_FROM_PROJECT_SUFFIX)
          project = secrets[0].split(LIST_ALL_FROM_PROJECT_SUFFIX).first
          [ "#{LIST_COMMAND} #{project.shellescape}", project ]
        end
      end
    end

View on GitHub (pinned to eee0083b38)

Solutions

  1. Run bws project list to confirm which project ids the token can actually see
  2. Use a project id from that list in the selector: kamal secrets fetch -a bitwarden-sm <project-id>/all
  3. In the Bitwarden Secrets Manager web vault, grant the machine account access to the project containing the secrets
  4. Retry once for transient network errors; upgrade the bws CLI if project list works but secret list errors

Example fix

# before
kamal secrets fetch -a bitwarden-sm 3f9a2b1c-xxxx/all
# => RuntimeError: Could not read secrets from Bitwarden Secrets Manager

# fix (terminal)
bws project list                       # copy a project id you can access
kamal secrets fetch -a bitwarden-sm 8d2e4f6a-yyyy/all
Defensive patterns

Strategy: try-catch

Validate before calling

abort "BWS_ACCESS_TOKEN not set or invalid" unless system("bws project list > /dev/null 2>&1")
projects = JSON.parse(`bws project list`).map { |p| p["id"] }
abort "project #{project_id} not accessible; available: #{projects.join(", ")}" unless projects.include?(project_id)

Type guard

def bws_project_accessible?(project_id)
  JSON.parse(`bws project list`).any? { |p| p["id"] == project_id }
end

Try / catch

begin
  adapter.fetch(["#{project_id}/all"])
rescue RuntimeError => e
  if e.message.include?("Could not read secrets from Bitwarden Secrets Manager")
    warn "run `bws project list` — the machine account may lack access to #{project_id}"
  end
  raise
end

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21). Data as JSON: /api/errors/1f64f7415e2b927e. Report an issue: GitHub.