unslothai/unsloth · error · TimeoutError
deadline reached before {method} {_redact_url(url)}
Error message
deadline reached before {method} {_redact_url(url)} What it means
Thrown in PlanReview.start (research-activity-panel.tsx:542-565) when a research run sits in awaiting_approval but latest.planHash is falsy after optionally saving plan edits via updateResearchPlan. The approval call (approveResearchRun) must send an opaque hash the backend computed over the plan; without it approval cannot prove the user reviewed that exact plan, so the flow aborts before the API call. Caught locally and shown as 'Could not start research'.
Source
Thrown at scripts/virustotal_scan.py:396
budget is spent so the caller can degrade to a warning row.
`max_attempts = 1` disables retrying, which is mandatory for a POST to a
single-use signed upload URL: replaying it can only ever be rejected.
`deadline` is checked BEFORE each attempt. One attempt can block for the
full socket timeout, so a loop that only checks afterwards can overrun the
caller's budget by minutes and get the whole step killed before it writes
a summary.
"""
headers = {"x-apikey": self._api_key, "accept": "application/json"}
if extra_headers:
headers.update(extra_headers)
backoff = self._request_interval if self._request_interval > 0 else 1.0
last_error = ""
for attempt in range(1, max_attempts + 1):
if deadline is not None and self._clock() >= deadline:
raise TimeoutError(f"deadline reached before {method} {_redact_url(url)}")
self._throttle(deadline)
# Re-check: pacing sleeps between the check above and the call below, so
# without this a request could start after the deadline and then block
# for the full socket timeout, overrunning the step's own budget.
if deadline is not None and self._clock() >= deadline:
raise TimeoutError(
f"deadline reached while pacing before {method} {_redact_url(url)}"
)
try:
# Clamp the socket budget to what is left. Without this a call that
# starts just before the deadline can still block for the full
# socket timeout and consume the whole cushion the step relies on
# to write its summary.
socket_timeout = _SOCKET_TIMEOUT
if deadline is not None:
socket_timeout = max(1.0, min(socket_timeout, deadline - self._clock()))
status, payload = self._transport(method, url, headers, body, socket_timeout)
except Exception as error: # network layer, DNS, TLS, truncated readView on GitHub (pinned to 203007d190)
Solutions
- Refresh the research run (refetch the session snapshot) so the store re-ingests a run with planHash populated.
- Update the studio backend to a version that emits plan_hash on awaiting_approval runs.
- If it persists, inspect the run object in the Network tab (GET /api/chat/research-runs/{id}) to confirm the backend omits plan_hash, and report it as a backend bug.
- Cancel and re-create the research run so a fresh plan (with hash) is generated.
Defensive patterns
Strategy: type-guard
Validate before calling
function canApprovePlan(run: ResearchRun): boolean {
return run.status === 'awaiting_approval' && typeof run.planHash === 'string' && run.planHash.length > 0;
} Type guard
function hasPlanHash(run: ResearchRun | undefined): run is ResearchRun & { planHash: string } {
return typeof run?.planHash === 'string' && run.planHash.length > 0;
} Try / catch
try {
await startResearch();
} catch (error) {
if (error instanceof Error && error.message === 'The research plan is missing its approval hash.') {
await refetchResearchRun(runId); // re-ingest a fresh snapshot, then retry
} else throw error;
} Prevention
- Disable the Start button until hasPlanHash(run) is true.
- Keep frontend and backend versions in lockstep so plan_hash is always emitted.
- Re-fetch the run snapshot when resuming research sessions restored from storage.
When it happens
Trigger: Run status awaiting_approval whose run.planHash is null/undefined — either the snapshot fetched from the backend never carried plan_hash, or updateResearchPlan returned a run object lacking the field (older backend version, or a run created before the hash feature). Editing the draft makes no difference; the check applies to `latest` in both branches.
Common situations: Frontend newer than the backend (backend predates the approval-hash field); stale run snapshot in useResearchRunStore that predates a backend restart/re-creation of the run; backend bug omitting plan_hash on the awaiting_approval transition.
Related errors
- lockfile not found: {path}
- {path}: not valid JSON: {exc}
- deadline reached while pacing before {method} {_redact_url(u
- family {getattr(fam, 'name', fam)!r} declares no prequant_fi
- load timed out (last progress: {prog})
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/2f6c722012d4b3cf.
Report an issue: GitHub.