{"record":{"id":"82db28a1695360a6","repo":"BloopAI/vibe-kanban","slug":"profile-key-not-found","errorCode":null,"errorMessage":"Profile key not found","messagePattern":"Profile key not found","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/web-core/src/shared/dialogs/settings/settings/McpSettingsSection.tsx","lineNumber":74,"sourceCode":"      } else if (Object.keys(profiles).length > 0) {\n        setSelectedProfile(Object.values(profiles)[0]);\n      }\n    }\n  }, [config?.executor_profile, profiles, selectedProfile]);\n\n  // Load MCP configuration when selected profile changes\n  useEffect(() => {\n    const loadMcpServersForProfile = async (profile: ExecutorProfile) => {\n      setMcpLoading(true);\n      setMcpError(null);\n      setMcpConfigPath('');\n\n      try {\n        const profileKey = profiles\n          ? Object.keys(profiles).find((key) => profiles[key] === profile)\n          : null;\n        if (!profileKey) {\n          throw new Error('Profile key not found');\n        }\n\n        if (!machineClient) {\n          throw new Error('Machine client is required');\n        }\n\n        const result = await machineClient.loadMcpServers({\n          executor: profileKey as BaseCodingAgent,\n        });\n        setMcpConfig(result.mcp_config);\n        const fullConfig = McpConfigStrategyGeneral.createFullConfig(\n          result.mcp_config\n        );\n        const configJson = JSON.stringify(fullConfig, null, 2);\n        setMcpServers(configJson);\n        setOriginalMcpServers(configJson);\n        setMcpConfigPath(result.config_path);\n      } catch (err: unknown) {","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/packages/web-core/src/shared/dialogs/settings/settings/McpSettingsSection.tsx#L56-L92","documentation":"loadMcpServersForProfile in McpSettingsSection throws 'Profile key not found' when it cannot find the key in the `profiles` record (Record<string, ExecutorProfile>) whose value reference-equals the currently selected profile. The key is needed as the `executor` identifier for machineClient.loadMcpServers. This happens when the selected ExecutorProfile object no longer comes from the current profiles map (stale reference after profiles reload).","triggerScenarios":"The effect runs with a selectedProfile set, but profiles is null/undefined, or no value in profiles === selectedProfile (strict equality) — typically profiles were refetched/replaced after the profile object was captured, or config.executor_profile references an executor absent from profiles.","commonSituations":"User system config reloaded while the settings page was open, replacing profile objects; selected executor removed/renamed in config; profiles still loading (null) when the effect fires; hot-module reload recreating objects in dev.","solutions":["Reload the settings page so selectedProfile re-initializes from the fresh profiles map","Verify the executor in config.executor_profile exists in profiles; fix ~/.vibe/config.toml if it names an unknown executor","Wait for profiles to load before rendering the MCP section (guard on profiles != null)","Harden the lookup: key profiles by executor name instead of object identity when wiring setSelectedProfile"],"exampleFix":"// before\nsetSelectedProfile(profiles[config.executor_profile.executor]); // captured object\n// after (lookup by key at use time)\nconst selectedProfileKey = selectedExecutorKey; // store the key, e.g. 'claude'\nconst profile = profiles?.[selectedExecutorKey];","handlingStrategy":"type-guard","validationCode":"// before the lookup, bail early instead of throwing:\nif (!profiles || Object.keys(profiles).length === 0) { setMcpLoading(false); return; }","typeGuard":"function findProfileKey(profiles: Record<string, ExecutorProfile> | null | undefined, profile: ExecutorProfile): string | null {\n  if (!profiles) return null;\n  const entry = Object.entries(profiles).find(([, p]) => p === profile);\n  return entry?.[0] ?? null;\n}","tryCatchPattern":"try {\n  const profileKey = findProfileKey(profiles, selectedProfile);\n  if (!profileKey) {\n    setMcpError('Profile configuration changed — reload settings to reselect the agent.');\n    return;\n  }\n  const result = await machineClient.loadMcpServers({ executor: profileKey as BaseCodingAgent });\n} catch (err) {\n  console.error('Error loading MCP servers:', err);\n}","preventionTips":["Store the selected profile's string key, not the object reference","Gate effects on `profiles` being loaded (non-null) before selecting/using a profile","Re-derive selectedProfile from profiles on every profiles update (derive state, don't cache)","In dev, note HMR can recreate profile objects; key-based selection avoids it"],"tags":["react","state","reference-equality"],"backgroundTag":"stale-object-reference","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}