{"record":{"id":"8fb5663add098213","repo":"MemPalace/mempalace","slug":"llm-endpoint-must-use-http-or-https-got-sch","errorCode":null,"errorMessage":"LLM_ENDPOINT must use http:// or https:// (got scheme {scheme!r})","messagePattern":"LLM_ENDPOINT must use http:// or https:// \\(got scheme (.+?)\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mempalace/closet_llm.py","lineNumber":112,"sourceCode":"\nclass LLMConfig:\n    \"\"\"Resolved LLM connection config. CLI flags > env vars.\"\"\"\n\n    def __init__(\n        self,\n        endpoint: Optional[str] = None,\n        key: Optional[str] = None,\n        model: Optional[str] = None,\n    ):\n        self.endpoint = (endpoint or os.environ.get(\"LLM_ENDPOINT\", \"\")).rstrip(\"/\")\n        self.key = key or os.environ.get(\"LLM_KEY\", \"\")\n        self.model = model or os.environ.get(\"LLM_MODEL\", \"\")\n        if self.endpoint:\n            # Privacy-by-architecture: reject file:// and other non-HTTP schemes\n            # so a misconfigured endpoint cannot exfiltrate local files.\n            scheme = urllib.parse.urlparse(self.endpoint).scheme.lower()\n            if scheme not in (\"http\", \"https\"):\n                raise ValueError(\n                    f\"LLM_ENDPOINT must use http:// or https:// (got scheme {scheme!r})\"\n                )\n\n    def missing(self) -> list:\n        missing = []\n        if not self.endpoint:\n            missing.append(\"LLM_ENDPOINT (or --endpoint)\")\n        if not self.model:\n            missing.append(\"LLM_MODEL (or --model)\")\n        # key is optional — local inference servers (Ollama, vLLM) often don't require one\n        return missing\n\n\ndef _call_llm(cfg: LLMConfig, source_file: str, wing: str, room: str, content: str):\n    \"\"\"Single LLM call via OpenAI-compatible /chat/completions.\n\n    Returns (parsed_json_dict_or_None, usage_dict_or_None).\n    \"\"\"","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/closet_llm.py#L94-L130","documentation":"Raised by the ClosetLLM constructor when LLM_ENDPOINT (or the --endpoint argument) has a URL scheme other than http or https. This is a deliberate privacy-by-architecture guard: the HTTP client could otherwise be pointed at file:// (or other schemes some clients honor), letting a misconfigured or injected endpoint read and exfiltrate local files. Only plain HTTP endpoints — local (Ollama, vLLM, LM Studio) or explicit BYOK cloud — are permitted.","triggerScenarios":"Constructing the LLM client with endpoint='file:///etc/passwd', LLM_ENDPOINT='unix:///run/ollama.sock', an empty-scheme value like 'localhost:11434' (parsed scheme becomes ''), or any gopher/ftp/file URL. Note the check fires only when the endpoint is non-empty.","commonSituations":"Forgetting the http:// prefix (LLM_ENDPOINT=localhost:11434 — urlparse yields scheme ''); trying to use a unix socket URL; a .env or hook config carrying a file:// path copied from another tool; typo like http:/ (single slash still parses as http, but htp:// does not).","solutions":["Set the endpoint with an explicit scheme: LLM_ENDPOINT=http://localhost:11434 for Ollama, https://... for cloud BYOK","If you intended a unix socket, point at the HTTP listen address of the local runtime instead","Audit the environment/hook config that injects LLM_ENDPOINT for a stale file:// value"],"exampleFix":"# before\nexport LLM_ENDPOINT=\"localhost:11434\"   # scheme '' -> ValueError\n\n# after\nexport LLM_ENDPOINT=\"http://localhost:11434\"","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\nendpoint = os.environ.get(\"LLM_ENDPOINT\", \"\")\nif endpoint and urlparse(endpoint).scheme.lower() not in (\"http\", \"https\"):\n    endpoint = \"http://\" + endpoint  # fix missing scheme for localhost runtimes\nos.environ[\"LLM_ENDPOINT\"] = endpoint","typeGuard":"def is_http_endpoint(url: str) -> bool:\n    return urlparse(url).scheme.lower() in (\"http\", \"https\")","tryCatchPattern":"try:\n    llm = ClosetLLM()\nexcept ValueError as exc:\n    if \"LLM_ENDPOINT\" in str(exc):\n        raise SystemExit(f\"fix LLM_ENDPOINT: {exc} (use http://host:port)\") from None\n    raise","preventionTips":["Always write local endpoints with the scheme: http://localhost:11434","Validate LLM_ENDPOINT in setup scripts before first use","Treat a non-HTTP scheme as a red flag for exfiltration misconfiguration, not just a typo"],"tags":["llm","configuration","security","environment","mempalace"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}