{"record":{"id":"a7817337c1880a3d","repo":"tinyhumansai/openhuman","slug":"could-not-reach-upserted-label-msg","errorCode":null,"errorMessage":"Could not reach ${upserted.label}: ${msg}","messagePattern":"Could not reach (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/components/settings/panels/AIPanel.tsx","lineNumber":3105,"sourceCode":"                  message: describeProviderVerificationFailure(slug, msg, t),\n                });\n              } else {\n                // Auth failure (wrong key), or a local runtime that isn't up:\n                // roll both stores back and reject so the user fixes it. Rollback\n                // failures are LOGGED, never swallowed — a silently failed\n                // key-clear is exactly what orphans a key on disk (#5339).\n                await flushCloudProviders(priorWireProviders).catch(rollbackErr =>\n                  console.warn(`[ai-settings] rollback flush failed slug=${slug}`, rollbackErr)\n                );\n                if (isKeyProvider) {\n                  await clearCloudProviderKey(slug).catch(rollbackErr =>\n                    console.warn(\n                      `[ai-settings] rollback clearCloudProviderKey failed slug=${slug}`,\n                      rollbackErr\n                    )\n                  );\n                }\n                throw new Error(`Could not reach ${upserted.label}: ${msg}`);\n              }\n            }\n          }\n        }\n\n        const nextDraft = {\n          ...draft,\n          cloudProviders: [...draft.cloudProviders.filter(p => p.slug !== slug), upserted],\n        };\n        await persist(nextDraft);\n        if (isCodexOAuth && slug === 'openai') {\n          await clearCloudProviderKey(slug);\n        }\n        if (slug === 'openai') {\n          setCodexAuthError(null);\n        }\n        setKeyDialogFor(null);\n        setPendingLocalLabel(null);","sourceCodeStart":3087,"sourceCodeEnd":3123,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/components/settings/panels/AIPanel.tsx#L3087-L3123","documentation":"Quick-add provider flow in AIPanel: after upserting the cloud provider and persisting its key, the panel probes the provider's /models endpoint; if the probe fails it first rolls back (re-flushes the prior provider wire list and clears the stored key with clearCloudProviderKey so no key is orphaned on disk, #5339) and then throws 'Could not reach <label>: <msg>' with the probe's own message appended. Unlike the dialog path (error 170) there is no skip-verification escape hatch here.","triggerScenarios":"Saving a provider whose /models probe rejects: wrong endpoint URL, invalid or expired API key, DNS/firewall block, provider reachable only behind a VPN, or a self-signed certificate failing TLS validation.","commonSituations":"Typo'd base URL (missing /v1, wrong region subdomain); key pasted with a trailing newline or from the wrong service; local gateways (Ollama/LM Studio) not running at save time; corporate proxies intercepting the probe.","solutions":["Verify the exact endpoint with curl - e.g. curl -H 'Authorization: Bearer <key>' <endpoint>/models - and fix URL or key accordingly","Re-enter and re-save the provider: rollback already removed it and cleared the key, so the previous state is restored","Check the console.warn '[ai-settings] provider /models probe failed' entry for the underlying msg","For local runtimes confirm the server is actually listening on that port before saving"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-flight the same request the panel will probe\nasync function providerReachable(endpoint: string, key: string): Promise<boolean> {\n  try {\n    const res = await fetch(`${endpoint.replace(/\\/$/, '')}/models`, {\n    headers: { Authorization: `Bearer ${key}` },\n  });\n    return res.ok;\n  } catch {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"// the throw happens after rollback - catch it, then let the user re-enter config\ncatch (err) {\n  const msg = err instanceof Error ? err.message : String(err);\n  if (msg.startsWith('Could not reach')) {\n    // provider + key were rolled back; reopen the form pre-filled for correction\n    setProviderSaveNotice({ slug, kind: 'probe-failed', message: msg });\n  }\n}","preventionTips":["curl the provider's /models with the key before saving - the panel probes exactly that","Assume the save is transactional: on probe failure the provider and key are rolled back, so fix and re-submit","For local model servers, confirm the process is listening before adding the provider"],"tags":["ai-settings","provider","probe","models-endpoint","rollback","network"],"backgroundTag":"api-endpoint-unreachable","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}