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
- 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).
- Verify the token works standalone before blaming kamal: `bws project list` must print JSON; if it errors, fix the token/CLI first.
- Check the token was not truncated or wrapped in quotes when pasted (re-copy from the console; tokens are long base64-ish strings).
- 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
- Provision BWS_ACCESS_TOKEN as a masked CI variable and assert it is present (ENV.key?('BWS_ACCESS_TOKEN')) before running kamal.
- Add a pre-deploy probe (`bws project list >/dev/null`) to fail with a clearer message than the adapter's generic raise.
- Track token rotation dates; regenerate machine-account tokens and update all environments together.
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Could not read #{secret_uuid} from Bitwarden Secrets Manager
- Could not read secrets from Bitwarden Secrets Manager
- Failed to login to Doppler
- could not login to gcloud
- Failed to login to LastPass
AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21).
Data as JSON: /api/errors/e1118db9e93396e2.
Report an issue: GitHub.