{"record":{"id":"b4ba167b9d4462f4","repo":"tinyhumansai/openhuman","slug":"mcp-install-missingrequired","errorCode":null,"errorMessage":"mcp.install.missingRequired","messagePattern":"mcp\\.install\\.missingRequired","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/src/components/channels/mcp/InstalledServerDetail.tsx","lineNumber":199,"sourceCode":"  // sets may be partial; the form requires every key so a reconnect never drops\n  // a required var (issue #3039 gap B6 — suggested values were never persisted).\n  const handleApplySuggestedEnv = useCallback(\n    (env: Record<string, string>) => {\n      log('suggested_env received, opening reconfigure form keys=%o', Object.keys(env));\n      setShowAssistant(false);\n      openReconfigure(env);\n    },\n    [openReconfigure]\n  );\n\n  const handleSaveReconfigure = useCallback(() => {\n    void runBusy(async () => {\n      // Replace-all semantics (update_env DELETEs then INSERTs): every key must\n      // have a value or the server loses required env on reconnect. Mirror the\n      // install dialog's validation.\n      for (const key of visibleEnvKeys) {\n        if (!reconfigValues[key]?.trim()) {\n          throw new Error(t('mcp.install.missingRequired').replace('{key}', key));\n        }\n      }\n      log('reconfigure save server_id=%s', server.server_id);\n      const result = await mcpClientsApi.updateEnv({\n        server_id: server.server_id,\n        env: reconfigValues,\n      });\n      setTools(result.tools ?? []);\n      if (result.status === 'unauthorized') {\n        // A 401 after reconfigure: show the actionable auth reason (use Sign in\n        // / token rejected / credential required) the same way the Connect\n        // dialog does — the raw 401 message is withheld server-side (#4289).\n        const key = authHintMessageKey(result.auth_hint);\n        throw new Error(key ? t(key) : t('mcp.detail.reconfigureReconnectFailed'));\n      }\n      if (result.status !== 'connected') {\n        throw new Error(result.error ?? t('mcp.detail.reconfigureReconnectFailed'));\n      }","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/components/channels/mcp/InstalledServerDetail.tsx#L181-L217","documentation":"Reconfigure uses replace-all semantics — updateEnv DELETEs then INSERTs every env row — so every visible env key must carry a value or the server would come back up missing required credentials. The save handler validates each reconfigValues[key] and throws this message (with {key} substituted) before any RPC is sent.","triggerScenarios":"Clicking Save in the reconfigure dialog with one or more env fields left blank or whitespace-only; the thrown message names the exact offending key.","commonSituations":"User clears a field intending to remove it (not supported — the key set is fixed by the server's declared env_keys); pasted values that are only spaces.","solutions":["Fill every required field with its credential value and save again — the error names the missing key","If a key is genuinely obsolete, remove it by reinstalling/reconfiguring the server's declared env, never by blanking it","Trim accidental whitespace before saving"],"exampleFix":"// before (lets updateEnv strip required env)\nawait mcpClientsApi.updateEnv({ server_id, env: reconfigValues });\n\n// after (validate replace-all input first)\nfor (const key of visibleEnvKeys) {\n  if (!reconfigValues[key]?.trim()) {\n    throw new Error(t('mcp.install.missingRequired').replace('{key}', key));\n  }\n}\nawait mcpClientsApi.updateEnv({ server_id, env: reconfigValues });","handlingStrategy":"validation","validationCode":"// Disable Save until every required env key is non-empty:\nconst canSave = visibleEnvKeys.every(k => (reconfigValues[k] ?? '').trim().length > 0);\n<Button disabled={!canSave} ...>","typeGuard":"const hasAllEnvValues = (\n  keys: readonly string[],\n  values: Record<string, string | undefined>\n): boolean => keys.every(k => !!values[k]?.trim());","tryCatchPattern":null,"preventionTips":["Remember updateEnv is replace-all: blanking a field deletes that env var server-side","Validate all visible fields client-side before calling mcpClientsApi.updateEnv","To remove a key, change the server's declared env_keys — not the values map"],"tags":["mcp","env","validation","reconfigure"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}