From 6ff2b2755614d7058250938bb652ea3952b3ab09 Mon Sep 17 00:00:00 2001 From: OpenCode Date: Mon, 17 Aug 2026 18:02:35 +0700 Subject: [PATCH] Fix MCP crash when DB key exists without NEXUS_MASTER_KEY KeyResolver now wraps getApiKey in try/catch and falls back to env instead of crashing startup. ProvidersStore.getApiKey returns undefined when the cipher is disabled rather than throwing. Server starts cleanly with env-only, and uses the encrypted DB key when the master key is present. --- NexusAI/packages/core/src/db/providers-store.ts | 7 ++++++- NexusAI/packages/core/src/security/resolver.ts | 13 ++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/NexusAI/packages/core/src/db/providers-store.ts b/NexusAI/packages/core/src/db/providers-store.ts index b81db80..200ee39 100644 --- a/NexusAI/packages/core/src/db/providers-store.ts +++ b/NexusAI/packages/core/src/db/providers-store.ts @@ -133,12 +133,17 @@ export class ProvidersStore { this.db.prepare('DELETE FROM providers WHERE name = ?').run(name); } - /** Return the decrypted API key for a provider, or undefined. */ + /** + * Return the decrypted API key for a provider, or undefined. + * Returns undefined (instead of throwing) when no master key is configured, + * so callers can gracefully fall back to env keys. + */ getApiKey(name: string): string | undefined { const row = this.db.prepare('SELECT api_key_enc FROM providers WHERE name = ?').get(name) as | { api_key_enc: string | null } | undefined; if (!row?.api_key_enc) return undefined; + if (!this.cipher.enabled) return undefined; return this.cipher.decrypt(row.api_key_enc); } diff --git a/NexusAI/packages/core/src/security/resolver.ts b/NexusAI/packages/core/src/security/resolver.ts index fff82bb..5eec369 100644 --- a/NexusAI/packages/core/src/security/resolver.ts +++ b/NexusAI/packages/core/src/security/resolver.ts @@ -45,7 +45,18 @@ export class KeyResolver { resolve(providerName: string, defaultBaseUrl: string): ResolvedProvider { const dbRow = this.store?.get(providerName); const envKey = this.envKeyFor(providerName); - const dbKey = this.store?.getApiKey(providerName); + // DB key may be unreadable (no master key / rotated key). Degrade gracefully: + // fall back to env instead of crashing startup. + let dbKey: string | undefined; + try { + dbKey = this.store?.getApiKey(providerName); + } catch (err) { + log.warn( + `could not read stored key for "${providerName}" (falling back to env)`, + err instanceof Error ? err.message : String(err) + ); + dbKey = undefined; + } const apiKey = envKey ?? dbKey; const keySource: ResolvedProvider['keySource'] = envKey ? 'env' : dbKey ? 'db' : 'none';