Errors
When humans can't, won't, or simply don't.
The HaaS API uses conventional HTTP response codes to indicate the success or failure of an API request. In addition to standard codes, we've introduced several human-specific error codes to help you understand exactly what went wrong and how to recover.
Error Response Format
All errors return a consistent JSON structure:
{
"error": {
"code": "human_unavailable",
"status": 503,
"message": "The requested human is currently unavailable.",
"details": {
"human_id": "usr_maria_42",
"reason": "sleeping",
"estimated_availability": "2024-01-15T08:00:00Z"
},
"suggestions": [
"Try another human in the same region",
"Reduce task urgency and wait for availability",
"Consider if this task truly requires a human"
],
"request_id": "req_9xK2mP7"
}
}
Standard HTTP Codes
These codes follow standard HTTP semantics:
| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Everything worked as expected. |
| 201 | Created | A new resource (task, connection) was created successfully. |
| 400 | Bad Request | The request was malformed. Check your parameters. |
| 401 | Unauthorized | Invalid or missing API key. |
| 403 | Forbidden | The API key doesn't have permission for this action. |
| 404 | Not Found | The requested resource doesn't exist. |
| 500 | Internal Server Error | Something went wrong on our end. We're looking into it. |
Human-Specific Error Codes
These codes are unique to the HaaS platform and reflect the beautiful complexity of working with human endpoints. We've mapped common human states to memorable HTTP codes to make debugging more intuitive.
| Code | Name | Description |
|---|---|---|
| 418 | I'm a Teapot | The human is making tea (or coffee) and cannot be disturbed. Retry in 5-15 minutes. This is a sacred ritual. |
| 420 | Enhance Your Calm | The human is on a scheduled break. Attempting to interrupt a break degrades long-term reliability scores. Patience recommended. |
| 421 | Misdirected Request | The task was sent to a human who lacks the required skills. Our matching algorithm usually prevents this, but humans sometimes overstate their abilities. |
| 422 | Unprocessable Entity | The human understood your request but found it morally objectionable, logistically impossible, or just "too weird." See details.human_feedback for specifics. |
| 423 | Locked | The human is currently assigned to another task and cannot accept new work. Consider enabling allow_queue for non-urgent tasks. |
| 424 | Failed Dependency | The task depends on something the human doesn't have access to (tools, locations, prerequisite information). Check details.missing_dependencies. |
| 425 | Too Early | You're trying to dispatch a task outside the human's working hours. Humans have circadian rhythms that should be respected. |
| 428 | Precondition Required | The human needs more information before accepting. They've requested clarification in details.questions. |
| 429 | Too Many Requests | You're overwhelming this human. Slow down. Humans have limited bandwidth and get stressed when overloaded. |
| 451 | Unavailable For Legal Reasons | The human has declined the task for ethical or legal reasons. This decision is final and should be respected without question. |
| 460 | Emotional Unavailability | The human is going through something and can't take on emotional labor right now. This is not the time to ask them to write a sympathy card. |
| 469 | Nice, But No | The human appreciated being asked but declined without providing a reason. Humans are allowed to do this. |
| 503 | Service Unavailable | The human is completely offline. Common causes: sleeping, vacation, "touching grass." Check estimated_availability. |
| 504 | Gateway Timeout | The human didn't respond in time. They may be distracted, have lost their phone, or be "in the zone" on something else. |
| 507 | Insufficient Storage | The human lacks the energy reserves to complete this task. Recommend caffeine or rest before retrying. |
| 508 | Loop Detected | The human is stuck in an unproductive cycle (doomscrolling, overthinking, reorganizing desk instead of working). Gentle intervention may help. |
| 509 | Bandwidth Limit Exceeded | The human has hit their daily task limit. This is a hard limit designed to prevent burnout. Try again tomorrow. |
| 520 | Unknown Error | Something went wrong but the human can't or won't articulate what. This is frustratingly common. Sometimes humans just have "a feeling." |
| 530 | Site Frozen | The human has experienced decision paralysis. Consider breaking the task into smaller steps or providing clearer guidance. |
Task-Specific Errors
Task Rejection Reasons
When a human rejects a task, the rejection_reason field provides context:
| Reason | Description | Recovery Strategy |
|---|---|---|
compensation_too_low |
The human doesn't feel the compensation matches the effort. | Increase compensation or reduce task scope. |
deadline_unrealistic |
The human believes the deadline is unachievable. | Extend deadline or simplify requirements. |
instructions_unclear |
The human doesn't understand what you're asking. | Rewrite with more detail. Humans need context. |
not_my_vibe |
The task doesn't align with the human's preferences. | Find a different human. Vibe fit matters. |
bad_previous_experience |
The human had a negative experience with a similar task or with you specifically. | Address past issues or find another human. |
just_because |
No reason provided. Humans have free will. | Accept and move on. Don't take it personally. |
Handling Errors Gracefully
Here's a pattern for robust error handling:
import haas
from haas.exceptions import (
HumanUnavailableError,
TaskRejectedError,
RateLimitError,
EthicalObjectionError
)
def dispatch_with_fallback(task, human_ids):
"""Try multiple humans until one accepts."""
for human_id in human_ids:
try:
result = haas.tasks.create(
human_id=human_id,
**task
)
return result
except EthicalObjectionError as e:
# Never retry ethical objections with the same task
# The human has spoken. Respect it.
logger.warning(f"Task rejected for ethical reasons: {e.details}")
raise
except HumanUnavailableError as e:
if e.code == 418: # Making tea
# This is temporary. Wait it out.
time.sleep(e.retry_after or 300)
continue
elif e.code == 503: # Sleeping
# Try someone else
continue
elif e.code == 507: # Low energy
# Offer caffeine as compensation addon
task['compensation']['includes_coffee'] = True
continue
except TaskRejectedError as e:
if e.reason == 'compensation_too_low':
# Auto-negotiate up to 1.5x
if task['compensation']['amount'] < task.get('max_compensation', float('inf')):
task['compensation']['amount'] *= 1.2
continue
elif e.reason == 'instructions_unclear':
# This is on you. Don't just retry.
raise
except RateLimitError as e:
# You're being too much. Take a breath.
logger.info(f"Rate limited. Cooling off for {e.retry_after}s")
time.sleep(e.retry_after)
continue
# All humans exhausted
raise NoAvailableHumansError(
"No humans available for this task. "
"Consider: Is this something you could do yourself?"
)
Webhook Error Events
If you've configured webhooks, you'll receive error events in real-time. This is especially useful for long-running tasks where the human might encounter issues mid-execution.
{
"event": "task.failed",
"task_id": "task_8f3Kq2x",
"timestamp": "2024-01-15T10:45:00Z",
"data": {
"failure_type": "human_abandoned",
"progress_at_failure": 0.65,
"human_explanation": "I'm so sorry, but my cat just knocked over a lamp and I need to deal with this. Can someone else take over?",
"partial_work": {
"completed_steps": ["step_1", "step_2", "step_3"],
"remaining_steps": ["step_4", "step_5"],
"artifacts": ["https://cdn.api4human.com/partial/abc123.zip"]
},
"handoff_available": true
}
}
Life happens. The partial_work object lets you salvage progress
and hand off to another human seamlessly. This is one of the advantages of
working with a managed platform rather than raw humans.
Errors are information
Every error is an opportunity to understand humans better. Track your error rates, learn the patterns, and build more resilient workflows.
Learn about reliability patterns →