Handle Python SDK errors without duplicating solves

By solvethree3 min read

A connection timeout doesn't prove that task creation failed. Keep the original task inputs and requestId; recover that request before creating another solve.

On this page

Catch task failures separately from HTTP errors

The synchronous Python SDK's solve method creates a task once and polls until it is ready or raises an error. Catch the specific exceptions before SolvethreeError so you can distinguish a failed solve from an interrupted HTTP request.

Set SOLVETHREE_API_KEY and SOLVETHREE_PROXY, then replace the example hostname and action with your target's values. The site needs an active workspace profile before it can be solved.

Python · distinguish failures and recover interrupted requests
import os
import uuid
from solvethree import Solvethree, ApiError, TaskFailedError, SolvethreeError

client = Solvethree(os.environ["SOLVETHREE_API_KEY"])
task = {"site": "accounts.example.com", "action": "login", "proxy": os.environ["SOLVETHREE_PROXY"], "requestId": str(uuid.uuid4())}
try:
    result = client.solve(task, timeout=180)
    print("Ready task:", result["id"])
    # Submit result["solution"] privately through the target application.
except TaskFailedError as error:
    print("Failed task:", error.task_id, "code:", error.error_code)
    # This task is terminal. Inspect it before starting different work.
except ApiError as error:
    print("HTTP status:", error.status, "retry delay:", error.retry_after)
    print("Recovery IDs:", error.request_id, error.task_id)
    # A known task_id means resume get_task; otherwise replay the exact task
    # with error.request_id. Do not generate a new logical request to recover it.
except SolvethreeError as error:
    print("Recovery IDs:", error.request_id, error.task_id)
    # Save these IDs and the original task inputs for explicit recovery.
  • TaskFailedError: the task reached terminal failed status. Inspect error.task_id and error.error_code; don't continue polling that task.
  • ApiError: an HTTP request failed. Inspect error.status and error.retry_after, which is measured in seconds. This isn't the same as a terminal task failure.
  • SolvethreeError: retain error.request_id and error.task_id for explicit recovery. Use whichever IDs are available rather than assuming creation never happened.

The example prints recovery information, but it doesn't persist it or perform recovery. In an integration that must survive a process restart, save the original task inputs and request ID before calling solve, then retain any returned task ID. Protect stored inputs that contain proxy credentials.

Resume by task ID; recover creation by request ID

  1. 1.

    If you have a task_id, resume get_task

    Retrieve that task instead of calling solve again. get_task accepts the task ID and a wait_ms argument; its default wait is 15000 milliseconds. Continue while status is pending or solving. Use the solution only when status is ready, and stop on failed.

  2. 2.

    If creation was interrupted before you received a task ID, replay create_task

    Send the identical task inputs with the same requestId, using the recovered request_id when needed. Reusing that ID with the same request returns the existing task; changing the inputs conflicts. Once you have its task ID, continue with get_task.

  3. 3.

    Don't generate another ID during recovery

    The example generates its UUID before calling solve. Keep that value when recovering; don't rerun task construction and accidentally replace it. A new ID represents a separate request, not a retry of the interrupted one.

The SDK generates a requestId if you omit it, exposes recovery IDs in errors, and never automatically retries task creation. During solve, it honors Retry-After for temporary polling failures within the solve deadline. If the call still raises, use the saved IDs to resume rather than starting another solve.

Recovery isn't a reason to repeat invalid requests

For ApiError, inspect the HTTP error before deciding to replay anything. Recovering interrupted creation is appropriate when the outcome is uncertain; blindly resending unchanged invalid inputs isn't.

For TaskFailedError, the task is finished. A retrieved failed task contains errorCode and error; the Python exception exposes the code as error.error_code. Use that information to decide what needs changing. For example, site-unreachable calls for investigating target reachability, not assuming the account lacks credit. Failed solves are refunded.

Run your first task.

Start with $5 in workspace credit.

Create a workspace