{"record":{"id":"4460464959c845cd","repo":"MemPalace/mempalace","slug":"openai-compat-provider-requires-llm-endpoint","errorCode":null,"errorMessage":"openai-compat provider requires --llm-endpoint","messagePattern":"openai-compat provider requires --llm-endpoint","errorType":"exception","errorClass":"LLMError","httpStatus":null,"severity":"error","filePath":"mempalace/llm_client.py","lineNumber":311,"sourceCode":"    ):\n        if api_key:\n            resolved_key = api_key\n            source: Optional[str] = \"flag\"\n        else:\n            env_key = os.environ.get(\"OPENAI_API_KEY\")\n            resolved_key = env_key or None\n            source = \"env\" if env_key else None\n        super().__init__(\n            model=model,\n            endpoint=endpoint,\n            api_key=resolved_key,\n            timeout=timeout,\n            api_key_source=source,\n        )\n\n    def _resolve_url(self) -> str:\n        if not self.endpoint:\n            raise LLMError(\"openai-compat provider requires --llm-endpoint\")\n        url = self.endpoint.rstrip(\"/\")\n        if url.endswith(\"/chat/completions\"):\n            return url\n        if not url.endswith(\"/v1\"):\n            url = f\"{url}/v1\"\n        return f\"{url}/chat/completions\"\n\n    def check_available(self) -> tuple[bool, str]:\n        if not self.endpoint:\n            return False, \"no --llm-endpoint configured\"\n        base = self.endpoint.rstrip(\"/\")\n        base = base.removesuffix(\"/chat/completions\").removesuffix(\"/v1\")\n        try:\n            req = Request(f\"{base}/v1/models\")\n            if self.api_key and (self.api_key_source != \"env\" or not self.is_external_service):\n                req.add_header(\"Authorization\", f\"Bearer {self.api_key}\")\n            with urlopen(req, timeout=5):\n                pass","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/llm_client.py#L293-L329","documentation":"LLMError raised by OpenAICompatProvider._resolve_url() when the provider was constructed without an endpoint. Unlike Ollama/Anthropic, the openai-compat provider has no default host — it targets any /v1/chat/completions server, so --llm-endpoint is mandatory.","triggerScenarios":"build_provider(\"openai-compat\", model=...) without passing endpoint (and no default resolution supplying one); the first classify() call then hits _resolve_url() and fails.","commonSituations":"Switching config from ollama to openai-compat (vLLM, LM Studio, llama.cpp server) and forgetting the endpoint flag; reading the endpoint from an env var that is unset so None propagates.","solutions":["Pass the endpoint: build_provider(\"openai-compat\", model=\"...\", endpoint=\"http://localhost:8000\")","With or without /v1 is fine — the provider appends /v1/chat/completions as needed","If the endpoint comes from config/env, validate it is non-empty at startup, not at first call"],"exampleFix":"# before\nprovider = build_provider(\"openai-compat\", model=\"Qwen/Qwen2.5-7B-Instruct\")\nprovider.classify(s, u)  # LLMError: openai-compat provider requires --llm-endpoint\n\n# after\nprovider = build_provider(\"openai-compat\", model=\"Qwen/Qwen2.5-7B-Instruct\", endpoint=\"http://localhost:8000\")","handlingStrategy":"validation","validationCode":"provider = build_provider(\"openai-compat\", model=m, endpoint=endpoint)\nif not provider.endpoint:\n    raise RuntimeError(\"--llm-endpoint is required for openai-compat\")","typeGuard":null,"tryCatchPattern":"from mempalace.llm_client import LLMError\n\ntry:\n    provider.classify(s, u)\nexcept LLMError as e:\n    if \"requires --llm-endpoint\" in str(e):\n        provider.endpoint = \"http://localhost:8000\"  # then retry\n    else:\n        raise","preventionTips":["Validate required config (provider + endpoint + key) at app startup, not at first LLM call","Fail fast in CLI flag parsing when provider == openai-compat and no endpoint is given"],"tags":["llm","configuration","openai-compat","endpoint"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}