What if I lose my passwords?

There is a very specific kind of panic that comes with a password manager problem.

Most software failures are annoying. A page does not load. A build fails. A button does nothing. You lose time, maybe patience.

A password vault feels different.

If the vault does not open, the question in your head is not "why is this app broken?" It is "what if I just lost access to everything?"

That is a brutal question because the whole promise of a password manager is concentration. You stop remembering hundreds of passwords. You stop reusing passwords. You stop storing secrets in notes, chats, screenshots, and browser autofill. You move the risk into one place because that one place is supposed to be safer.

So when that one place looks empty, or keeps loading, or says "unexpected error", it feels personal.

This week I had to troubleshoot exactly that kind of problem.

The web vault worked. The browser extension and desktop client did not. Login succeeded, but the entries did not show. At first, that is the worst possible shape of failure: enough access to prove the account exists, not enough access to trust that the data is there.

The first job was not to upgrade anything.

The first job was to prove whether the passwords still existed.

The vault was not empty

I started with the boring checks.

Was the service running? Yes.

Was the domain pointing to the right server? Yes.

Was the database still there? Yes.

Then came the check that actually mattered: count the vault records from the server side.

The database still had one active user, hundreds of active entries, folders, devices, and deleted entries. A direct API reader from the gateway could fetch and decrypt real items. The server was not serving an empty vault. The data was there.

That changed the problem.

This was not a disaster recovery case. It was a client compatibility and sync case.

That distinction matters. If the database is gone, the conversation is about restore points. If the database is intact, the conversation is about why a specific client cannot process what the server is sending.

The user experience looks similar in both cases: "my passwords are missing."

The engineering reality is completely different.

The first failure was compatibility

The logs showed the first real error:

POST /identity/accounts/prelogin/password => 404 Not Found

That endpoint matters because newer Bitwarden clients call it during login. The Vaultwarden server was older than the clients expected, so the desktop app and browser extension were asking for an API route the server did not have.

The web vault still worked because it was using a compatible flow. That is why the browser page could show the vault while the extension could fail.

This is one of the strange parts of self-hosted software. "It works in the web app" does not always mean "it works for every client." The web app, browser extension, desktop app, and mobile app can move at different speeds. If the server lags behind the clients, login can break in one place and still work in another.

The fix was straightforward, but it still needed discipline.

Before touching the service, I checked where the container was running, confirmed the mounted data volume, and made a backup of the SQLite database and the full data directory. Only then did I upgrade Vaultwarden.

After the upgrade, the same endpoint returned `200 OK`.

That was progress.

It was not the whole fix.

Login worked, sync still hung

After the server upgrade, login worked in the extension. The error changed shape. The extension authenticated, started sync, then kept loading.

The logs were clean:

POST /identity/connect/token => 200 OK
GET /api/sync?excludeDomains=true => 200 OK
GET /api/accounts/profile => 200 OK

That is the moment where "the logs are green" can trick you.

Green logs only mean the server responded. They do not mean the client liked the response. They do not mean every encrypted item inside the response can be decrypted. They do not mean the UI finished building its local vault.

So the next check was not "did `/api/sync` return 200?"

The next check was: "Can we decrypt every item name in the sync payload?"

The answer was no.

The vault had 626 active entries at the time. Most were fine. One active entry had a malformed encrypted name. It failed with an `Incorrect padding` error.

That is a small number with a big effect.

One bad row can be enough to break a client if the client expects every encrypted field to decode cleanly. The web vault may tolerate it. A desktop app or extension may not.

So we quarantined that one active item.

Not deleted blindly. Quarantined with a per-row backup and a reversible database change. The active count went down by one. A fresh sync showed zero corrupt active entries.

The extension still hung.

That was annoying, but useful.

The trash still travels

The next check found the last piece.

The active vault was clean, but the sync response still included malformed entries from the trash. They were already deleted items, but they still traveled in the sync payload because clients need to know what changed.

That is easy to forget. Deleted does not always mean gone. In many systems, deleted means "marked as deleted and still synchronized until retention or purge."

The vault had nine malformed deleted entries. Eight were old. One was the active item we had just moved to trash.

At that point the choice was clear, but it was also sensitive: hard-delete items from a password vault.

Even if the items are already in the trash, that is not something to do casually. The right move was to export those rows first, record the hashes, then delete exactly those nine rows and nothing else.

After the purge, the fresh sync had:

  • 754 total entries
  • 625 active entries
  • 129 deleted entries
  • 15 folders
  • zero malformed cipher names

The important number is the last one.

The server returned `200 OK`, and the sync payload could be processed end to end.

Backups are not the same as recovery

This is the part I keep coming back to.

Most people think the question is: "Do I have a backup?"

That is not enough.

The better question is: "Can I prove I can recover?"

For a password manager, that means a few concrete things.

You need a database backup, but you also need to know where the live data volume is mounted. You need to know which node the container is running on. You need the server version. You need the domain configuration. You need a way to test sync without depending only on the browser UI.

And you need a way to tell the difference between:

  • the database is gone
  • the server is outdated
  • the client cache is broken
  • a single encrypted item is malformed
  • deleted items are still poisoning sync

Those are five different problems. They can all look like "my passwords are missing."

This is why operational work needs receipts.

When a vault is involved, "I restarted it" is not good enough. "It looks fine" is not good enough. A good recovery path leaves evidence:

  • backup path
  • backup hash
  • service version
  • database counts before and after
  • sync endpoint result
  • payload integrity check
  • rollback path

That evidence is boring when everything works.

It is gold when something fails.

The real safety net

A password manager is a single point of responsibility.

That does not make it bad. The alternative is often worse: reused passwords, forgotten accounts, secrets scattered across browsers and chats, and no clear way to rotate anything.

But if the password manager is the center, the center needs care.

For a self-hosted vault, I would want at least this:

  • automated database and data-volume backups
  • backup hashes written somewhere outside the container
  • regular restore drills on a separate machine
  • a known working CLI or API reader for emergency access
  • update monitoring for Vaultwarden and Bitwarden client changes
  • a small runbook for client sync problems

The runbook matters because the failure is emotional. When your vault looks empty, you do not want to invent the process under pressure.

The process should already exist.

First, prove the data exists. Then prove the server can serve it. Then prove the client can process it. Then change one thing at a time.

That sequence keeps panic from driving the work.

What I learned from this one

The lesson was not "always update your password manager."

Updating helped, but it only fixed the first failure. The harder part was finding the malformed records that made a successful sync useless for the client.

The better lesson is this: a password vault can fail in layers.

The account can authenticate while the vault still cannot render. The server can return `200 OK` while the client still hangs. The database can be intact while a few encrypted rows are bad enough to poison the experience.

So the answer to "what if I lose my passwords?" should not be a vague promise that backups exist.

The answer should be a tested recovery path.

Can we find the database? Can we back it up? Can we restore it? Can we count the entries? Can we sync them? Can we decrypt enough metadata to know the payload is healthy? Can we isolate bad rows without touching the rest?

That is the difference between hope and recovery.

In this case, the passwords were not lost.

The vault was there. The server needed an upgrade. The sync payload needed cleanup. The scary part was real, but it was diagnosable.

That is the kind of result you want from infrastructure.

Not magic. Not blind trust.

Evidence, backups, and a path back.

← Back to Blog