Graphify-Labs/graphify · error · ValueError

label response is not parseable JSON: {text[:120]!r}

Error message

label response is not parseable JSON: {text[:120]!r}

What it means

ValueError raised when a community-label batch response from the LLM is neither parseable JSON nor salvageable by the regex fallback. The parser first tries full JSON; on failure it recovers complete quoted cid/name pairs (issue #1690: truncated replies mid-object should not kill the whole batch); only when even that finds no pairs does it raise, echoing the first 120 chars of the raw text.

Source

Thrown at graphify/llm.py:2935

    data: dict | None = None
    try:
        parsed = json.loads(cleaned)
        if isinstance(parsed, dict):
            data = parsed
    except (json.JSONDecodeError, ValueError):
        data = None
    if data is None:
        # Salvage: pull the complete "<cid>": "<name>" pairs directly. A model
        # can truncate its reply mid-object (a stingy token budget or a preamble
        # eating the completion), which used to hard-fail the whole batch with
        # e.g. `Expecting value: line 1 column 6` on a `{"0":` fragment (#1690).
        # Recovering the pairs that DID arrive labels those communities instead
        # of dropping the entire batch to placeholders.
        pairs = re.findall(r'"?(-?\d+)"?\s*:\s*"([^"\\]*(?:\\.[^"\\]*)*)"', cleaned)
        if pairs:
            data = {k: v for k, v in pairs}
        else:
            raise ValueError(f"label response is not parseable JSON: {text[:120]!r}")
    out: dict[int, str] = {}
    for cid in labeled_cids:
        name = data.get(str(cid))
        if name is None:
            name = data.get(cid)
        if isinstance(name, str) and name.strip():
            out[cid] = name.strip()
    return out


def _label_batch_with_retry(
    batch_cids: list[int],
    batch_lines: list[str],
    *,
    backend: str,
    model: str | None,
    depth: int = 0,
    max_depth: int = 3,

View on GitHub (pinned to 7fe58b0b0f)

Solutions

  1. Retry the batch - truncation-based failures are often transient; _label_batch_with_retry exists for this.
  2. Increase the LLM max-token budget for labeling so the JSON object fits.
  3. If the echoed text is prose/refusal, tighten the labeling prompt or switch model/backend.
  4. If the echoed text looks like HTML, fix base_url - you are not talking to the model you think.

Example fix

# before
labels = label_batch(cids)   # ValueError: label response is not parseable JSON

# after - retry via the library's retry wrapper and a bigger budget
labels = _label_batch_with_retry(cids, max_tokens=2048)
# fallback: accept placeholders for unlabeled communities
Defensive patterns

Strategy: retry

Try / catch

try:
    labels = label_batch(cids)
except ValueError as exc:
    if "not parseable JSON" in str(exc):
        labels = {cid: f"community-{cid}" for cid in cids}  # placeholder fallback
        log.warning("LLM labels unparseable; using placeholders: %s", exc)
    else:
        raise

Prevention

When it happens

Trigger: Parsing a label response where json.loads fails AND the pair-regex finds no complete '<cid>': '<name>' structures at all (llm.py:2928-2935) - the reply is pure prose, a fully truncated fragment like '{"0":', or HTML/error text from a gateway.

Common situations: Reasoning/thinking models answering with prose instead of JSON; tiny max_tokens budgets truncating before the first pair; a misrouted base_url returning an HTML error page; prompts mutated by content filters so the model refuses instead of labeling.

Related errors


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