Eugeny/tabby · warning · Error
Vault unlock cancelled
Error message
Vault unlock cancelled
What it means
Thrown by `VaultService.getPassphrase` when the unlock modal is dismissed or returns a falsy result (user clicked cancel/closed the dialog/pressed ESC). Since there is no remembered passphrase, the only way to obtain one is the modal; cancelling it leaves no passphrase to proceed, so the operation aborts.
Source
Thrown at tabby-core/src/services/vault.service.ts:183
}
if (_rememberedPassphrase) {
_rememberedPassphrase = passphrase
}
return wrapPromise(this.zone, encryptVault(vault, passphrase))
}
async save (vault: Vault, passphrase?: string): Promise<void> {
await this.ready$.toPromise()
this.store = await this.encrypt(vault, passphrase)
this.contentChanged.next()
}
async getPassphrase (): Promise<string> {
if (!_rememberedPassphrase) {
const modal = this.ngbModal.open(UnlockVaultModalComponent)
const result = await modal.result.catch(() => null)
if (!result) {
throw new Error('Vault unlock cancelled')
}
const { passphrase, rememberFor } = result
setTimeout(() => {
_rememberedPassphrase = null
// avoid multiple consequent prompts
}, Math.max(1000, rememberFor * 60000))
_rememberedPassphrase = passphrase
}
return _rememberedPassphrase!
}
async getSecret (type: string, key: VaultSecretKey): Promise<VaultSecret|null> {
await this.ready$.toPromise()
const vault = await this.load()
if (!vault) {
return null
}View on GitHub (pinned to 14e2d60b9b)
Solutions
- Wrap the calling operation in try/catch and treat 'Vault unlock cancelled' as a user-initiated cancel (skip the action, do not retry automatically).
- If the operation is mandatory, re-prompt the user with clearer messaging rather than silently failing.
- Pre-unlock the vault at app start (via a login flow) so dependent operations already have `_rememberedPassphrase` set.
- For headless contexts, supply the passphrase programmatically to `vault.decrypt(store, passphrase)` / `encrypt(vault, passphrase)` to bypass the modal entirely.
Example fix
// before
const vault = await this.vault.load() // may throw 'Vault unlock cancelled'
// after - treat cancel as a soft abort
try {
const vault = await this.vault.load()
return vault
} catch (e) {
if (e instanceof Error && e.message === 'Vault unlock cancelled') {
return null // user chose not to unlock; degrade gracefully
}
throw e
} Defensive patterns
Strategy: try-catch
Validate before calling
async function tryWithPassphrase (vault: VaultService, passphrase: string | null, fn: () => Promise<any>) {
if (passphrase) {
// pass explicitly to bypass the modal entirely
return fn()
}
return fn() // will trigger modal; caller must handle cancel
} Try / catch
try {
return await vault.load()
} catch (e) {
if (e instanceof Error && e.message === 'Vault unlock cancelled') {
return null // graceful: user declined to unlock
}
throw e
} Prevention
- Unlock the vault once at session start so subsequent operations have a remembered passphrase.
- Always treat 'Vault unlock cancelled' as a benign user choice, not an error to retry.
- Pass an explicit passphrase to decrypt/encrypt in automated flows to avoid the modal.
- Distinguish cancel from wrong-passphrase (different error path) in the caller.
When it happens
Trigger: Any vault operation that needs the passphrase (decrypt, encrypt, getSecret, addSecret, retrieveFile via VaultFileProvider) when `_rememberedPassphrase` is null and the user dismisses the `UnlockVaultModalComponent`. The modal's `.result` promise rejects on dismiss, which is caught and normalized to null.
Common situations: User cancels the master-passphrase prompt because they forgot it, do not want to unlock right now, or hit ESC by accident; an automated/scripted flow has no UI to answer the modal.
Related errors
AI-assisted analysis of Eugeny/tabby@14e2d60b9b (2026-08-12).
Data as JSON: /api/errors/33fb216035351538.
Report an issue: GitHub.