{"record":{"id":"f6c4826147448acb","repo":"Graphify-Labs/graphify","slug":"no-api-key-for-backend-backend-set-format-b-f6c482","errorCode":null,"errorMessage":"No API key for backend '{backend}'. Set {_format_backend_env_keys(backend)}.","messagePattern":"No API key for backend '(.+?)'\\. Set (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"graphify/llm.py","lineNumber":2581,"sourceCode":"    Used by lightweight callers (e.g. `graphify.dedup` LLM tiebreaker) that\n    don't need the full extraction prompt or JSON-shaped output. Mirrors the\n    backend dispatch logic of `extract_files_direct` but skips the\n    `_EXTRACTION_SYSTEM` prompt and JSON parsing.\n\n    Previously `graphify.dedup` imported a `_call_llm` symbol that did not\n    exist in this module, so the LLM tiebreaker silently no-op'd on\n    `ImportError` (F-038). Adding the function here re-enables it.\n    \"\"\"\n    if backend not in BACKENDS:\n        raise ValueError(f\"Unknown backend {backend!r}\")\n    cfg = BACKENDS[backend]\n    key = _get_backend_api_key(backend)\n    if not key and backend == \"ollama\":\n        ollama_url = _resolve_ollama_base_url(cfg.get(\"base_url\", \"\"))\n        _validate_ollama_base_url(ollama_url)\n        key = \"ollama\"\n    if not key and backend not in (\"bedrock\", \"claude-cli\"):\n        raise ValueError(\n            f\"No API key for backend '{backend}'. Set {_format_backend_env_keys(backend)}.\"\n        )\n    mdl = model or _default_model_for_backend(backend)\n\n    def _rec(inp, out) -> None:\n        if usage_out is not None:\n            usage_out[\"input\"] = usage_out.get(\"input\", 0) + int(inp or 0)\n            usage_out[\"output\"] = usage_out.get(\"output\", 0) + int(out or 0)\n\n    if backend == \"claude\":\n        try:\n            import anthropic\n        except ImportError as exc:\n            raise ImportError(_backend_pkg_hint(\"anthropic\", \"anthropic\")) from exc\n        client = anthropic.Anthropic(api_key=key, base_url=cfg[\"base_url\"], timeout=_resolve_api_timeout(), max_retries=_resolve_max_retries())\n        resp = client.messages.create(\n            model=mdl,\n            max_tokens=max_tokens,","sourceCodeStart":2563,"sourceCodeEnd":2599,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/llm.py#L2563-L2599","documentation":"Raised by the lightweight `call_llm` helper when no API key resolves for the selected backend: `_get_backend_api_key(backend)` returned nothing and no key parameter was supplied. As with error 55, `bedrock` and `claude-cli` are exempt (AWS chain / CLI's own auth) and `ollama` gets a placeholder with a warning. Note this helper takes no `api_key=` override in the shown signature — unlike `extract_files_direct` — so the env var is the only route.","triggerScenarios":"Calling `call_llm(prompt, backend=\"openai\")` (or any key-based backend) in a process where that backend's key env var is unset; using `graphify.dedup` LLM tiebreaker without the same credentials used for extraction.","commonSituations":"Running dedup in a separate process/CI job that forgot to export the API key; key env var named for a different provider than the backend string passed; env lost through service managers.","solutions":["Export the backend's key env var (the names appear via `_format_backend_env_keys(backend)`, e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) in the calling process.","Reuse the exact same backend+credentials that succeeded for extraction when running the dedup tiebreaker.","Switch the helper to `bedrock` or `claude-cli` if you want credential-chain/CLI auth without env keys."],"exampleFix":"# before\nsubprocess.run([sys.executable, \"-m\", \"graphify.dedup\", ...])  # child env lacks OPENAI_API_KEY\n\n# after\nenv = {**os.environ, \"OPENAI_API_KEY\": os.environ[\"OPENAI_API_KEY\"]}\nsubprocess.run([sys.executable, \"-m\", \"graphify.dedup\", ...], env=env)","handlingStrategy":"validation","validationCode":"from graphify.llm import _get_backend_api_key\n\nif not _get_backend_api_key(\"openai\"):\n    raise SystemExit(\"call_llm needs OPENAI_API_KEY in this process's environment\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["The lightweight call_llm path resolves keys from env only — export the same credentials you used for extraction before running dedup tiebreakers.","Run extraction and dedup in the same process/env, or propagate API key env vars explicitly to child processes."],"tags":["api-key","configuration","environment","backend","dedup"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}