affaan-m/ECC · error · ValueError

empty response

Error message

empty response

What it means

extract_json in the distillation script parses model output into JSON. It raises ValueError('empty response') immediately when the model returned an empty or whitespace-only string, before attempting fence or brace extraction.

Solutions

  1. Retry the model call; if the provider is intermittently returning empty completions, add a retry with backoff before parsing
  2. Check the completion's finish_reason / stop reason and token limits (max_tokens) that may have suppressed output
  3. Log the raw response (length, finish reason) to distinguish filtering, truncation, and auth/quota issues
  4. Guard the call site: only call extract_json after verifying the response text is non-empty

Example fix

// before
text = client.complete(prompt)
result = extract_json(text)
// after
text = client.complete(prompt)
if not text or not text.strip():
    raise RuntimeError(f'model returned empty completion (finish_reason={text.finish_reason})')
result = extract_json(text)
Defensive patterns

Strategy: retry

Validate before calling

if not completion or not completion.text.strip():
    raise RuntimeError('model returned empty completion; skipping parse')

Type guard

def has_text(completion):
    return bool(completion and completion.text and completion.text.strip())

Try / catch

for attempt in range(3):
    text = client.complete(prompt)
    if has_text(text):
        break
    time.sleep(2 ** attempt)
else:
    raise RuntimeError('empty completion after 3 attempts')
result = extract_json(text)

Prevention

When it happens

Trigger: The LLM call returned an empty completion (empty string or only whitespace/newlines), which is passed to extract_json from describe().

Common situations: Provider returning empty completions due to content filters, max_tokens=0 or exhausted quota, network truncation, or a misconfigured prompt yielding no output; retrying against an unreachable endpoint that returns empty bodies.

Understand the failure class

Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/d000758cf33f221b. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-distillation/scripts/distill.py:88

    "'}'. Do not wrap it in a code fence. Do not add commentary. Every key "
    "listed must be present; use a short string (or list of strings) for each."
)


# ---------------------------------------------------------------------------
# JSON extraction / repair
# ---------------------------------------------------------------------------


def extract_json(text: str) -> dict:
    """Pull a JSON object out of a model reply.

    Models wrap JSON in code fences and preambles even when told not to, so a
    bare ``json.loads`` fails on output that is otherwise perfectly good.
    Fenced content is tried first, then the outermost balanced ``{...}``.
    """
    if not text or not text.strip():
        raise ValueError("empty response")

    candidates: list[str] = []
    for m in re.finditer(r"```(?:json)?\s*(.+?)```", text, re.DOTALL | re.IGNORECASE):
        candidates.append(m.group(1))
    candidates.append(text)

    for chunk in candidates:
        chunk = chunk.strip()
        try:
            obj = json.loads(chunk)
            if isinstance(obj, dict):
                return obj
        except json.JSONDecodeError:
            pass
        span = _balanced_object(chunk)
        if span:
            try:
                obj = json.loads(span)

View on GitHub (pinned to 8321021c54)