{"record":{"id":"0005fcf9ab0e4cb0","repo":"tinyhumansai/openhuman","slug":"endpoint-must-start-with-http-or-https","errorCode":null,"errorMessage":"Endpoint must start with http:// or https://","messagePattern":"Endpoint must start with http:// or https://","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/src/components/settings/panels/AIPanel.tsx","lineNumber":2987,"sourceCode":"      // /models probe is made — auth + execution both go through the local\n      // `claude` CLI. Mirrors the Codex skip, but also skips model listing.\n      const isCliLogin = credentialMode === 'cli_login';\n      setBusyAction(`toggle-${localLabel ? localLabel.toLowerCase().replace(/\\s/g, '') : slug}`);\n      // A fresh attempt on THIS provider clears only its own prior advisory —\n      // an advisory about a different provider must survive (#5341).\n      setProviderSaveNotice(prev => (prev?.slug === slug ? null : prev));\n\n      try {\n        const trimmed = value.trim();\n        // For `endpoint_key` (OMLX), the endpoint URL arrives via `endpointOverride`\n        // (the dialog's endpoint field) and `trimmed` is the API key. For plain\n        // `endpoint` runtimes, `trimmed` itself is the endpoint URL.\n        const rawEndpoint = isEndpointKey ? (endpointOverride ?? '').trim() : trimmed;\n        const endpoint = isLocalRuntime\n          ? (() => {\n              const url = new URL(rawEndpoint);\n              if (!/^https?:$/.test(url.protocol)) {\n                throw new Error('Endpoint must start with http:// or https://');\n              }\n              if (url.pathname === '' || url.pathname === '/') {\n                url.pathname = '/v1';\n              }\n              return url.toString().replace(/\\/$/, '');\n            })()\n          : defaultEndpointFor(slug);\n\n        const upserted: CloudProvider = {\n          id: `p_${slug}_${Math.random().toString(36).slice(2, 7)}`,\n          slug,\n          label: localLabel ?? BUILTIN_PROVIDER_META[slug]?.label ?? slug,\n          endpoint,\n          authStyle: authStyleForSlug(slug),\n          // CLI-login providers hold no API key — reflect that honestly so\n          // the entry matches its reloaded (has_api_key === false) shape.\n          maskedKey: maskKeyLabel(!isCliLogin),\n        };","sourceCodeStart":2969,"sourceCodeEnd":3005,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/components/settings/panels/AIPanel.tsx#L2969-L3005","documentation":"Custom local-runtime provider save parses the endpoint field with new URL() and requires the protocol to match ^https?:$; explicit non-http schemes like ws://, ftp:// or file:// fail here. A missing scheme never reaches this check — new URL('host:port') throws Invalid URL first. An empty path is then defaulted to /v1.","triggerScenarios":"Entering ws://localhost:8080, ftp://host, or any explicitly non-http scheme in the local runtime endpoint field; pasting a WebSocket URL for an OpenAI-compatible server.","commonSituations":"User copies a ws:// URL from server logs, or an internal non-HTTP scheme; missing-scheme typos surface as 'Invalid URL' instead.","solutions":["Use the full http:// or https:// form, e.g. http://localhost:1234/v1","For ws-only servers, enter their plain HTTP base — the /models probe is ordinary HTTP","Omit /v1 if unsure: the code appends /v1 automatically when the path is empty or /"],"exampleFix":"// before\nendpoint: 'ws://localhost:8080' // throws: protocol not http/https\n\n// after\nendpoint: 'http://localhost:8080' // path defaults to /v1","handlingStrategy":"validation","validationCode":"const isValidHttpEndpoint = (s: string): boolean => {\n  if (!/^https?:\\/\\//i.test(s.trim())) return false;\n  try { new URL(s.trim()); return true; } catch { return false; }\n};\nif (!isValidHttpEndpoint(input)) { /* block submit with hint */ }","typeGuard":"const isHttpUrl = (u: URL): boolean => u.protocol === 'http:' || u.protocol === 'https:';","tryCatchPattern":"try {\n  new URL(rawEndpoint);\n} catch {\n  // missing scheme lands here as 'Invalid URL' — prompt for http(s):// prefix\n}\nif (!/^https?:$/.test(new URL(rawEndpoint).protocol)) {\n  // non-http scheme — reject with the same guidance\n}","preventionTips":["Always paste endpoints with an explicit http:// or https:// scheme","Use the server's HTTP base URL, not its ws:// address, for OpenAI-compatible runtimes","Let the dialog default the path to /v1 rather than guessing provider-specific prefixes"],"tags":["validation","url","ai-provider","settings"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}