Graphify-Labs/graphify · error · ValueError

No API key for backend '{backend}'. Set {_format_backend_env

Error message

No API key for backend '{backend}'. Set {_format_backend_env_keys(backend)}.

What it means

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.

Source

Thrown at graphify/llm.py:2581

    Used by lightweight callers (e.g. `graphify.dedup` LLM tiebreaker) that
    don't need the full extraction prompt or JSON-shaped output. Mirrors the
    backend dispatch logic of `extract_files_direct` but skips the
    `_EXTRACTION_SYSTEM` prompt and JSON parsing.

    Previously `graphify.dedup` imported a `_call_llm` symbol that did not
    exist in this module, so the LLM tiebreaker silently no-op'd on
    `ImportError` (F-038). Adding the function here re-enables it.
    """
    if backend not in BACKENDS:
        raise ValueError(f"Unknown backend {backend!r}")
    cfg = BACKENDS[backend]
    key = _get_backend_api_key(backend)
    if not key and backend == "ollama":
        ollama_url = _resolve_ollama_base_url(cfg.get("base_url", ""))
        _validate_ollama_base_url(ollama_url)
        key = "ollama"
    if not key and backend not in ("bedrock", "claude-cli"):
        raise ValueError(
            f"No API key for backend '{backend}'. Set {_format_backend_env_keys(backend)}."
        )
    mdl = model or _default_model_for_backend(backend)

    def _rec(inp, out) -> None:
        if usage_out is not None:
            usage_out["input"] = usage_out.get("input", 0) + int(inp or 0)
            usage_out["output"] = usage_out.get("output", 0) + int(out or 0)

    if backend == "claude":
        try:
            import anthropic
        except ImportError as exc:
            raise ImportError(_backend_pkg_hint("anthropic", "anthropic")) from exc
        client = anthropic.Anthropic(api_key=key, base_url=cfg["base_url"], timeout=_resolve_api_timeout(), max_retries=_resolve_max_retries())
        resp = client.messages.create(
            model=mdl,
            max_tokens=max_tokens,

View on GitHub (pinned to 7fe58b0b0f)

Solutions

  1. 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.
  2. Reuse the exact same backend+credentials that succeeded for extraction when running the dedup tiebreaker.
  3. Switch the helper to `bedrock` or `claude-cli` if you want credential-chain/CLI auth without env keys.

Example fix

# before
subprocess.run([sys.executable, "-m", "graphify.dedup", ...])  # child env lacks OPENAI_API_KEY

# after
env = {**os.environ, "OPENAI_API_KEY": os.environ["OPENAI_API_KEY"]}
subprocess.run([sys.executable, "-m", "graphify.dedup", ...], env=env)
Defensive patterns

Strategy: validation

Validate before calling

from graphify.llm import _get_backend_api_key

if not _get_backend_api_key("openai"):
    raise SystemExit("call_llm needs OPENAI_API_KEY in this process's environment")

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14). Data as JSON: /api/errors/f6c4826147448acb. Report an issue: GitHub.