basecamp/kamal · error · RuntimeError

Could not authenticate to Bitwarden Secrets Manager. Did you

Error message

Could not authenticate to Bitwarden Secrets Manager. Did you set a valid access token?

What it means

Kamal's Bitwarden Secrets Manager adapter raises this in #login after running `bws project list` and observing a non-zero exit status ($?.success? is false). The bws CLI authenticates solely through the machine-account access token it reads from the environment (BWS_ACCESS_TOKEN), so a missing, malformed, revoked, or expired token makes every command fail. The error surfaces during Kamal::Secrets::Adapters::BitwardenSecretsManager#fetch, after check_dependencies! has already confirmed the CLI is installed.

Source

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

    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

    def run_command(command, session: nil)
      full_command = [ "bws", command ].join(" ")
      `#{full_command}`
    end

    def login(account)
      run_command("project list")
      raise RuntimeError, "Could not authenticate to Bitwarden Secrets Manager. Did you set a valid access token?" unless $?.success?
    end

    def check_dependencies!
      raise RuntimeError, "Bitwarden Secrets Manager CLI is not installed" unless cli_installed?
    end

    def cli_installed?
      `bws --version 2> /dev/null`
      $?.success?
    end
end

View on GitHub (pinned to eee0083b38)

Solutions

  1. Create a machine account and access token in the Bitwarden Secrets Manager admin console, then export it: export BWS_ACCESS_TOKEN=<token> (put it in the environment kamal runs in, e.g. .kamal/secrets or CI masked variables).
  2. Verify the token works standalone before blaming kamal: `bws project list` must print JSON; if it errors, fix the token/CLI first.
  3. Check the token was not truncated or wrapped in quotes when pasted (re-copy from the console; tokens are long base64-ish strings).
  4. If the machine account or token was revoked, generate a new access token and update every environment that runs kamal.

Example fix

# before: token missing/expired, `kamal secrets pull` raises
#   Could not authenticate to Bitwarden Secrets Manager...

# after: provision and export a machine-account token
#   export BWS_ACCESS_TOKEN="0000..."
bws project list  # sanity check: must print JSON
kamal secrets pull
Defensive patterns

Strategy: try-catch

Validate before calling

require "open3"

def bws_authenticated?
  _out, _err, status = Open3.capture3("bws", "project", "list",
                                      { BWS_ACCESS_TOKEN: ENV.fetch("BWS_ACCESS_TOKEN", "") })
  status.success?
end

raise "Set a valid BWS_ACCESS_TOKEN before deploying" unless bws_authenticated?

Try / catch

begin
  secrets = adapter.fetch(%w[RAILS_MASTER_KEY], account: nil)
rescue RuntimeError => e
  raise "Bitwarden auth failed: check BWS_ACCESS_TOKEN (rotated/revoked?) — #{e.message}" if e.message.include?("authenticate to Bitwarden")
  raise
end

Prevention

When it happens

Trigger: Calling adapter.fetch(secrets, account: ...) (directly or via `kamal secrets pull` with secrets adapter bitwarden_secrets_manager) when: BWS_ACCESS_TOKEN is unset in the shell running kamal; the token was copied with stray whitespace/quotes or truncated; the machine account was deleted or its access token revoked in the Bitwarden admin console; or the token belongs to a different organization/server than bws is pointed at. run_command("project list") returns non-zero and the raise fires unless $?.success?.

Common situations: CI pipelines where the token lives in a CI secret variable that was never exported; rotating tokens after a security review and forgetting to update the local .env/kamal env; self-hosted Bitwarden with a BWS server URL mismatch; token generated for a user account instead of a machine account (bws only accepts machine-account access tokens).

Understand the failure class

Related errors


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