unslothai/unsloth · error · TimeoutError

analysis {analysis_id} did not complete before the deadline

Error message

analysis {analysis_id} did not complete before the deadline

What it means

TimeoutError raised inside wait_for_analysis when the monotonic clock reaches the caller-supplied deadline before the VirusTotal analysis for the given analysis id reports status "completed". It is a deadline violation on the polling loop, not an HTTP failure: every poll succeeded but the remote analysis was still queued/running.

Source

Thrown at scripts/virustotal_scan.py:542

                # attempt, not a dead end: VirusTotal may well be analysing the file
                # already. Raising straight out would report the asset unavailable
                # after we had paid the disclosure cost of sending it, so spend the
                # remaining attempt on a fresh signed URL instead.
                last_error = RuntimeError("VirusTotal upload did not return an analysis id")
                if attempt < attempts:
                    continue
                raise last_error
            return analysis_id

        raise RuntimeError(f"VirusTotal upload failed after {attempts} attempt(s): {last_error}")

    def wait_for_analysis(self, analysis_id: str, deadline: float) -> object:
        """Poll until the analysis completes or the caller's deadline passes."""
        while True:
            # Checked inside request() too, but raising the analysis-specific message
            # here keeps the summary row readable.
            if self._clock() >= deadline:
                raise TimeoutError(f"analysis {analysis_id} did not complete before the deadline")
            _, payload = self.request(
                "GET", f"{API_ROOT}/analyses/{analysis_id}", deadline = deadline
            )
            attributes = _attributes(payload)
            if attributes.get("status") == "completed":
                return payload
            if self._request_interval <= 0:
                # With throttling disabled (premium key) the loop would otherwise spin.
                self._sleep(1.0)


def _build_multipart(path: Path) -> tuple[bytes, str]:
    """Encode `path` as a single-part multipart/form-data body under the field `file`."""
    boundary = f"----UnslothDesktopScan{uuid.uuid4().hex}"
    head = (
        f"--{boundary}\r\n"
        f'Content-Disposition: form-data; name="file"; filename="{path.name}"\r\n'
        "Content-Type: application/octet-stream\r\n\r\n"

View on GitHub (pinned to 203007d190)

Solutions

  1. Increase the deadline passed to wait_for_analysis, sized to the uploaded file's size (large files take minutes, not seconds).
  2. Retain the analysis id: the upload itself succeeded, so you can poll the same id again later instead of re-uploading and paying the disclosure cost twice.
  3. Check the analysis status via GET /analyses/{id} manually to see whether it is queued vs failed.
  4. If this happens consistently for small files, check VirusTotal service status and your request interval/throttling settings.

Example fix

# before
deadline = time.monotonic() + 60
payload = vt.wait_for_analysis(analysis_id, deadline)

# after
deadline = time.monotonic() + 15 * 60  # large uploads take minutes
payload = vt.wait_for_analysis(analysis_id, deadline)
Defensive patterns

Strategy: retry

Validate before calling

deadline = max(deadline, time.monotonic() + size_based_budget(path))  # e.g. 60s per 100MB, floor 5min

Try / catch

try:
    payload = vt.wait_for_analysis(analysis_id, deadline)
except TimeoutError:
    # upload succeeded; persist analysis_id and poll again later instead of re-uploading
    schedule_retry(analysis_id)

Prevention

When it happens

Trigger: Polling GET {API_ROOT}/analyses/{analysis_id} until the deadline while VirusTotal still returns status 'queued' or 'running'; a very large uploaded file whose analysis exceeds the deadline; VirusTotal backend backlogs; a deadline computed too tight relative to the file size.

Common situations: Scanning multi-hundred-MB model or dataset artifacts where VirusTotal analysis routinely takes many minutes; deadline derived from a global run timeout that leaves only seconds for analysis; periods of VirusTotal slowness; request interval misconfigured so polls are too sparse to observe completion in time.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/c2629adeb8d828a0. Report an issue: GitHub.