Skip to content

A failed URL download is uploaded to the API as if it were the file #22

Description

@deepgori

Describe the bug

ReadStreamUtils.url_to_stream fetches a URL and returns its body without checking the HTTP status:

# deepinfra/utils/read_stream.py
response = httpx.get(url, follow_redirects=True)
return BytesIO(response.content)

If the URL 404s (or 500s, or returns a login page), the error page's bytes become the "file". FormDataUtils.get_form_data then uploads that HTML as the blob field, so AutomaticSpeechRecognition.generate({"audio": url, ...}) sends an HTML document where the audio should be. The user sees either a confusing model-side error or a bad result, with nothing pointing at the URL that failed.

This is the one place in the SDK where a failed HTTP response is neither raised nor surfaced: the client in deepinfra/clients/deepinfra.py maps every error status onto the APIStatusError hierarchy in _exceptions.py, and file_to_stream lets a missing local path raise FileNotFoundError. A dead URL is the same class of mistake as a wrong path, but it fails silently instead.

Steps to reproduce

import http.server, threading
from deepinfra.utils.read_stream import ReadStreamUtils

class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        body = b"<html><body><h1>404 Not Found</h1></body></html>"
        self.send_response(404)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)
    def log_message(self, *a): pass

srv = http.server.HTTPServer(("127.0.0.1", 0), Handler)
threading.Thread(target=srv.serve_forever, daemon=True).start()
url = f"http://127.0.0.1:{srv.server_address[1]}/missing-audio.mp3"

print(ReadStreamUtils.get_read_stream(url).read())

Output on deepinfra 0.3.0:

b'<html><body><h1>404 Not Found</h1></body></html>'

The same bytes reach the request body through FormDataUtils.get_form_data({"audio": url}, blob_keys=["audio"]).

Expected behaviour

A failed download should raise, with the URL and status in the message, rather than producing a stream of the error page.

Suggested fix

Call response.raise_for_status() and translate the failure into the SDK's own error type, so callers can catch it alongside every other SDK failure:

response = httpx.get(url, follow_redirects=True, timeout=...)
if response.is_error:
    raise APIStatusError(
        f"Failed to download {url}",
        status_code=response.status_code,
        response=response,
    )

A transport failure (DNS, connect, TLS) would similarly map to APIConnectionError, matching _map_transport_error in the client.

Happy to send a PR with this and a regression test if the approach looks right.

Smaller, related points in the same file, happy to split out or leave

  • get_read_stream treats any string starting with http as a URL, so a local file named http_notes.wav is fetched as a URL.
  • A data: URI without a comma raises IndexError from input_data.split(",")[1] rather than a clear error.
  • BaseModel._warn_about_missing_api_key prints a warning and returns "", so a missing key sends Authorization: Bearer and surfaces as a server-side 401 instead of the SDK's own AuthenticationError, whose message tells the user exactly which environment variable to set.

Environment

deepinfra 0.3.0 (main at 3a6c0b9), Python 3.12, macOS.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions