basecamp/kamal · error · RuntimeError

Could not read #{item} from Bitwarden

Error message

Could not read #{item} from Bitwarden

What it means

For each requested Bitwarden item the adapter runs `bw get item <name>`; a non-zero exit raises RuntimeError "Could not read <item> from Bitwarden". The command succeeded at the CLI level (session valid) but no item with that exact name/uuid was returned — typically a missing or unsynced item, or a name mismatch.

Source

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

      if status["status"] == "locked"
        session = run_command("unlock --raw", raw: true).presence
        status = run_command("status", session: session)
      end

      raise RuntimeError, "Failed to login to and unlock Bitwarden" unless status["status"] == "unlocked"

      run_command("sync", session: session, raw: true)
      raise RuntimeError, "Failed to sync Bitwarden" unless $?.success?

      session
    end

    def fetch_secrets(secrets, from:, account:, session:)
      {}.tap do |results|
        items_fields(prefixed_secrets(secrets, from: from)).each do |item, fields|
          item_json = run_command("get item #{item.shellescape}", session: session, raw: true)
          raise RuntimeError, "Could not read #{item} from Bitwarden" unless $?.success?
          item_json = JSON.parse(item_json)
          if fields.any?
            results.merge! fetch_secrets_from_fields(fields, item, item_json)
          elsif item_json.dig("login", "password")
            results[item] = item_json.dig("login", "password")
          elsif item_json["fields"]&.any?
            fields = item_json["fields"].pluck("name")
            results.merge! fetch_secrets_from_fields(fields, item, item_json)
          else
            raise RuntimeError, "Item #{item} is not a login type item and no fields were specified"
          end
        end
      end
    end

    def fetch_secrets_from_fields(fields, item, item_json)
      fields.to_h do |field|
        item_field = item_json["fields"].find { |f| f["name"] == field }

View on GitHub (pinned to eee0083b38)

Solutions

  1. Verify the item resolves: bw get item <name> in the same shell — if it errors, the name or access is wrong
  2. Run bw sync to pull the latest vault, then retry the fetch
  3. Use the item's exact name (or its UUID) in the kamal secrets fetch arguments, matching case
  4. Confirm the logged-in account/organization can see the item in the Bitwarden web vault

Example fix

# before
kamal secrets fetch -a bitwarden --account me@example.com MASTER_KEY
# => RuntimeError: Could not read MASTER_KEY from Bitwarden

# fix (terminal)
bw sync
bw get item myapp-master-key    # find the real name
kamal secrets fetch -a bitwarden --account me@example.com myapp-master-key
Defensive patterns

Strategy: validation

Validate before calling

items.each do |item|
  ok = system("bw get item #{item.shellescape} > /dev/null 2>&1")
  warn "bitwarden item `#{item}` not found/accessible — check name and bw sync" unless ok
end

Type guard

def bitwarden_item_exists?(name)
  system("bw get item #{name.shellescape} > /dev/null 2>&1")
end

Try / catch

begin
  secrets = adapter.fetch(names, account: account)
rescue RuntimeError => e
  if e.message.include?("from Bitwarden")
    names.each { |n| warn "check item: bw get item #{n}" unless system("bw get item #{n.shellescape} > /dev/null 2>&1") }
  end
  raise
end

Prevention

When it happens

Trigger: Requesting secret RAILS_MASTER_KEY when the vault item is named myapp-master-key; item exists in a different organization/collection not accessible; item was created after the last bw sync; requesting with an id instead of the name kamal resolved.

Common situations: Renaming items in the Bitwarden web vault without updating deploy scripts; new teammate whose account lacks access to the organization holding the item; item created moments before fetch on another machine.

Related errors


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