666ghj/MiroFish · error · FetchError

GitHub API response could not be processed

Error message

GitHub API response could not be processed

What it means

Catch-all for any non-FetchError, non-OSError/TimeoutError exception raised while handling a successful response (the with response block): geturl()/getcode() returning unexpected types, _read_response raising something other than FetchError, or defects in custom response objects supplied by a custom opener. It separates 'response obtained but processing crashed unexpectedly' from the typed failure paths.

Source

Thrown at scripts/fetch_star_count.py:111

    except (urllib.error.URLError, TimeoutError, OSError):
        raise FetchError("GitHub API network request failed") from None
    except Exception:
        raise FetchError("GitHub API request could not be started") from None

    try:
        with response:
            if response.geturl() != API_URL:
                raise FetchError("GitHub API redirect was refused")
            status = response.getcode()
            if status != 200:
                raise _status_error(status)
            payload = _read_response(response)
    except FetchError:
        raise
    except (TimeoutError, OSError):
        raise FetchError("GitHub API response could not be read") from None
    except Exception:
        raise FetchError("GitHub API response could not be processed") from None

    try:
        document = json.loads(payload)
    except (UnicodeDecodeError, json.JSONDecodeError, ValueError):
        raise FetchError("GitHub API returned malformed JSON") from None
    if not isinstance(document, dict):
        raise FetchError("GitHub API response had an unexpected shape")

    count = document.get("stargazers_count")
    if type(count) is not int or count < 0:
        raise FetchError("GitHub API returned an invalid stargazers_count")
    return count


def main(argv: list[str] | None = None) -> int:
    arguments = sys.argv[1:] if argv is None else argv
    if arguments:
        print("error: this command accepts no arguments", file=sys.stderr)

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Reproduce outside the wrapper (temporarily remove the except Exception) to see the original traceback and fix the underlying object.
  2. Make custom openers/responses implement the minimal real interface: geturl(), getcode(), headers (email.message.Message-like), read(n), context manager.
  3. In tests, raise OSError/TimeoutError subclasses for I/O failures so they map to the precise messages instead.
Defensive patterns

Strategy: try-catch

Validate before calling

def response_is_httpresponse_like(obj: object) -> bool:
    """Minimal interface fetch_star_count needs from a response object."""
    return all(
        callable(getattr(obj, name, None))
        for name in ("geturl", "getcode", "read")
    ) and hasattr(obj, "headers") and hasattr(obj, "__enter__")

Try / catch

try:
    count = fetch_star_count(token, opener=fake_opener)
except FetchError as exc:
    if exc.args[0] == "GitHub API response could not be processed":
        # response handler crashed unexpectedly; call handler directly to debug
        raise

Prevention

When it happens

Trigger: Passing a mock opener whose response object lacks proper geturl/getcode/read semantics and raises AttributeError; _read_response hitting a header object whose .get returns a value that int() mishandles in an unanticipated way; future urllib behavior changes.

Common situations: Test doubles not matching the http.client.HTTPResponse interface; custom openers wrapping other HTTP clients incompletely.

Related errors


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/9505a3ee0d90703e. Report an issue: GitHub.