{"record":{"id":"c33c0be6f9493202","repo":"basecamp/kamal","slug":"failed-to-login-to-lastpass","errorCode":null,"errorMessage":"Failed to login to LastPass","messagePattern":"Failed to login to LastPass","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"lib/kamal/secrets/adapters/last_pass.rb","lineNumber":6,"sourceCode":"class Kamal::Secrets::Adapters::LastPass < Kamal::Secrets::Adapters::Base\n  private\n    def login(account)\n      unless loggedin?(account)\n        `lpass login #{account.shellescape}`\n        raise RuntimeError, \"Failed to login to LastPass\" unless $?.success?\n      end\n    end\n\n    def loggedin?(account)\n      `lpass status --color never`.strip == \"Logged in as #{account}.\"\n    end\n\n    def fetch_secrets(secrets, from:, account:, session:)\n      secrets = prefixed_secrets(secrets, from: from)\n      items = `lpass show #{secrets.map(&:shellescape).join(\" \")} --json`\n      raise RuntimeError, \"Could not read #{secrets} from LastPass\" unless $?.success?\n\n      items = JSON.parse(items)\n\n      {}.tap do |results|\n        items.each do |item|\n          results[item[\"fullname\"]] = item[\"password\"]\n        end","sourceCodeStart":1,"sourceCodeEnd":24,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/secrets/adapters/last_pass.rb#L1-L24","documentation":"Raised in LastPass#login when `lpass login <account>` exits non-zero. The adapter first checks loggedin? by comparing `lpass status --color never` output to \"Logged in as <account>.\"; if that exact string does not match, it attempts an interactive lpass login, which requires a TTY and pinentry for the master password. Any mismatch or non-interactive failure triggers this RuntimeError.","triggerScenarios":"adapter.fetch(secrets, account: 'user@example.com') when: not logged in at all (lpass status shows 'Not logged in'); logged in as a DIFFERENT account than --account (string mismatch causes a pointless re-login attempt); lpass login cannot prompt (no TTY/pinentry in CI/ssh); or the master password entry fails/aborts. Note $?.success? is checked after the backtick, so a failed or cancelled login raises.","commonSituations":"CI containers with no pinentry and no cached session (~/.lpass); account email typo or changed corporate email so status string never matches; trusted-machine session expired; running kamal from a script where lpass cannot open the prompt and exits non-zero.","solutions":["Log in once interactively on the machine as the SAME account: `LPASS_DISABLE_PINENTRY=1 lpass login user@example.com` (reads password from stdin) or normal `lpass login`, so `lpass status --color never` prints exactly 'Logged in as user@example.com.'.","Ensure --account matches the logged-in account byte-for-byte (the adapter's loggedin? does an exact string compare including the trailing period).","For automation, use lpass's non-interactive login: `echo \"$LPASS_PASSWORD\" | LPASS_DISABLE_PINENTRY=1 lpass login user@example.com` in a pre-step before kamal runs.","Install/verify pinentry if interactive login aborts (brew install pinentry, set LPASS_PINENTRY)."],"exampleFix":"# before: CI has no lpass session\n#   -> RuntimeError: Failed to login to LastPass\n\n# after: non-interactive pre-login step in CI\n- run: echo \"$LPASS_PASSWORD\" | LPASS_DISABLE_PINENTRY=1 lpass login team@company.com\n- run: kamal secrets pull --account team@company.com","handlingStrategy":"try-catch","validationCode":"require \"open3\"\n\ndef lpass_logged_in_as?(account)\n  out, _err, _st = Open3.capture3(\"lpass\", \"status\", \"--color\", \"never\")\n  out.strip == \"Logged in as #{account}.\"\nend\n\nabort \"Not logged into LastPass as #{ACCOUNT}\" unless lpass_logged_in_as?(ACCOUNT)","typeGuard":null,"tryCatchPattern":"begin\n  secrets = adapter.fetch(names, account: ACCOUNT)\nrescue RuntimeError => e\n  if e.message == \"Failed to login to LastPass\"\n    raise \"lpass login failed: pre-authenticate (LPASS_DISABLE_PINENTRY=1 lpass login #{ACCOUNT}) and re-run\"\n  end\n  raise\nend","preventionTips":["Log in non-interactively in a pre-step: `echo \"$LPASS_PASSWORD\" | LPASS_DISABLE_PINENTRY=1 lpass login <account>`.","Ensure the --account string exactly matches `lpass status` output (adapter compares the full sentence).","In CI, inject LPASS_PASSWORD as a secret and never assume a cached ~/.lpass session."],"tags":["ruby","kamal","lastpass","authentication","secrets","pinentry","headless","cli-exit-code"],"backgroundTag":"cli-authentication-failed","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}