Eugeny/tabby · error · Error
Vault is locked
Error message
Vault is locked
What it means
Thrown by the VaultFileProvider's `selectAndStoreFile` when `vault.load()` returns null, which happens when the vault store is unset (`VaultService.store === null`) - i.e. the vault feature is disabled or no StoredVault has been created yet. The provider refuses to store a file because there is nowhere secure to put it.
Source
Thrown at tabby-core/src/services/vault.service.ts:283
prefix = 'vault://'
constructor (
private vault: VaultService,
private platform: PlatformService,
private selector: SelectorService,
private zone: NgZone,
) {
super()
}
async isAvailable (): Promise<boolean> {
return this.vault.isEnabled()
}
async selectAndStoreFile (description: string): Promise<string> {
const vault = await this.vault.load()
if (!vault) {
throw new Error('Vault is locked')
}
const files = vault.secrets.filter(x => x.type === VAULT_SECRET_TYPE_FILE) as VaultFileSecret[]
if (files.length) {
const result = await this.selector.show<VaultFileSecret|null>('Select file', [
{
name: 'Add a new file',
icon: 'fas fa-plus',
result: null,
},
...files.map(f => ({
name: f.key.description,
icon: 'fas fa-file',
result: f,
})),
]).catch(() => null)
if (result) {
return `${this.prefix}${result.key.id}`
}View on GitHub (pinned to 14e2d60b9b)
Solutions
- Always go through `FileProvidersService.selectProvider()` (or `selectAndStoreFile`) so availability is checked and the user is prompted to set a passphrase.
- Before calling VaultFileProvider directly, guard with `if (!await vaultProvider.isAvailable()) { await vault.setEnabled(true, passphrase) }`.
- Re-enable the vault: `vault.setEnabled(true, passphrase)` creates the StoredVault, after which `load()` returns a Vault.
- Catch the error and fall back to an alternative provider (e.g. ElectronFileProvider) when the vault is unavailable.
Example fix
// before
async selectAndStoreFile (description: string): Promise<string> {
const vault = await this.vault.load()
if (!vault) throw new Error('Vault is locked')
...
}
// after - ensure availability and fall back gracefully
async selectAndStoreFile (description: string): Promise<string> {
let vault = await this.vault.load()
if (!vault) {
if (!this.vault.isEnabled()) await this.vault.setEnabled(true, await promptPassphrase())
vault = await this.vault.load()
}
if (!vault) throw new Error('Vault is locked')
... Defensive patterns
Strategy: validation
Validate before calling
async function ensureVaultUnlocked (vault: VaultService): Promise<Vault> {
let v = await vault.load()
if (!v) {
if (!vault.isEnabled()) await vault.setEnabled(true, await promptForPassphrase())
v = await vault.load()
}
if (!v) throw new Error('Vault is locked')
return v
} Try / catch
try {
return await vaultProvider.selectAndStoreFile(description)
} catch (e) {
if (e instanceof Error && e.message === 'Vault is locked') {
await vault.setEnabled(true, await promptForPassphrase())
return await vaultProvider.selectAndStoreFile(description)
}
throw e
} Prevention
- Always call FileProvidersService.selectProvider (not a specific provider) so availability is enforced.
- Re-check availability immediately before the call to catch races where the vault was disabled.
- Disable UI affordances that store secrets when the vault is disabled.
- Provide a fallback provider when the vault is unavailable.
When it happens
Trigger: Calling `selectAndStoreFile` on VaultFileProvider while `VaultService.isEnabled()` is false OR `store` is null. Note `isAvailable()` returns `vault.isEnabled()`, so this is reachable when a caller invokes the provider directly bypassing `selectProvider`, or when the vault becomes disabled between the availability check and the call.
Common situations: User disabled the vault after a provider was selected; a code path calls VaultFileProvider directly instead of going through FileProvidersService.selectProvider; race where the vault is locked/disabled mid-session.
Related errors
AI-assisted analysis of Eugeny/tabby@14e2d60b9b (2026-08-12).
Data as JSON: /api/errors/95b2f96c5662888f.
Report an issue: GitHub.