Skip to main content
Understanding how to handle errors in Skyvern is crucial for building reliable automations. This guide covers error patterns, status codes, and recovery strategies.

Run statuses

Every Skyvern run has a status that indicates its current state or outcome:
StatusWhat it meansCommon causes
queuedRun is waiting to startConcurrency limit reached, sequential run lock active
runningRun is actively executing
completedRun finished successfullyTask completed, but output may still be incorrect
failedSystem error occurredBrowser crash, network failure, infrastructure issue
terminatedAI gave up on the taskCAPTCHA, login blocked, element not found, page unavailable
timed_outExceeded max_steps limitTask too complex, AI got stuck in a loop
canceledManually stopped by user

Check run status

You can check the status of a run using the SDK or API:
import os
from skyvern import Skyvern

client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))

run = await client.get_run(run_id)

print(f"Status: {run.status}")
print(f"Failure reason: {run.failure_reason}")
print(f"Step count: {run.step_count}")
print(f"Output: {run.output}")

Polling for completion

To wait for a run to complete, you can poll the status in a loop:
import asyncio
from skyvern import Skyvern

client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))

while True:
    run = await client.get_run(run_id)
    if run.status in ["completed", "failed", "terminated", "timed_out", "canceled"]:
        break
    await asyncio.sleep(5)

if run.status == "completed":
    print(f"Success! Output: {run.output}")
else:
    print(f"Failed with status: {run.status}")
    print(f"Reason: {run.failure_reason}")
For production use, consider using webhooks instead of polling to get notified when runs complete.

Understanding failure reasons

When a run fails or terminates, the failure_reason field provides details about what went wrong:

Common failure reasons

  • “Element not found” — Element was not detected in the visible elements tree
  • “Element not visible” — Element exists but is not visible (hidden, off-screen)
  • “Element not interactable” — Element is covered by another element or disabled
Fix: Add scrolling instructions, check for dynamic loading, verify element is not in an iframe
  • “Login failed” — Credentials rejected or login flow changed
  • “CAPTCHA detected” — CAPTCHA appeared and couldn’t be solved
  • “MFA required” — Multi-factor authentication needed but not configured
Fix: Verify credentials, use browser profiles, configure TOTP for 2FA
  • “Required field missing” — Schema field marked as required but not found
  • “Invalid data format” — Extracted data doesn’t match expected schema type
  • “Extraction timeout” — Data extraction took too long
Fix: Make fields optional where appropriate, add clearer descriptions, verify data exists on page

Error handling in workflows

Workflows support error handling at the block level, allowing you to define fallback behaviors when a block fails.

Continue on error

You can configure a block to continue the workflow even if it fails:
from skyvern import Skyvern

client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))

# When creating a workflow block, set continue_on_failure=True
# This allows the workflow to proceed even if this specific block fails

Conditional error handling

Use conditional blocks to handle specific error scenarios:
  1. Run a block that might fail
  2. Add a validation block to check if it succeeded
  3. Use a conditional block to branch based on the validation result
  4. Execute recovery logic in the failure branch

Retry logic

For transient errors, you can implement retry logic:
import asyncio
from skyvern import Skyvern

client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))

max_retries = 3
for attempt in range(max_retries):
    try:
        result = await client.run_task(
            prompt="Your task prompt",
            url="https://example.com"
        )
        if result.status == "completed":
            print(f"Success on attempt {attempt + 1}")
            break
    except Exception as e:
        print(f"Attempt {attempt + 1} failed: {e}")
        if attempt < max_retries - 1:
            await asyncio.sleep(5)  # Wait before retrying
        else:
            print("Max retries exceeded")
            raise

Custom error codes

You can define custom error codes in your task to map specific scenarios to programmatic error handling:
from skyvern import Skyvern

client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))

result = await client.run_task(
    prompt="Fill out the form",
    url="https://example.com/form",
    error_code_mapping={
        "CAPTCHA_DETECTED": "Page shows 'Verify you are human'",
        "OUT_OF_STOCK": "Page shows 'Out of stock' or 'Unavailable'",
        "INVALID_COUPON": "Error message contains 'invalid coupon'"
    }
)

if result.status == "terminated":
    # Check if any custom error code was triggered
    print(f"Custom error: {result.error_code}")

Best practices

Always check status

Never assume a run completed successfully. Always check the status field.

Log failure reasons

Capture and log failure_reason for debugging and monitoring.

Use webhooks in production

Webhooks are more efficient than polling for production workloads.

Implement retry logic

Add retries for transient errors like network failures or timeouts.

Next steps

Common Issues

Step-by-step debugging for failed runs

Common Issues

Quick solutions to frequently encountered problems

Build docs developers (and LLMs) love