Skip to content

Error Handling

How to handle errors from the Tajiri Vision API.


HTTP Status Codes

Code Name Description
200 OK Request successful
400 Bad Request Invalid input (file type, size, parameters)
422 Unprocessable Entity Validation error
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server-side error
503 Service Unavailable AI service temporarily unavailable

Error Response Format

All errors return a JSON object with a detail field:

{
  "detail": "Error message describing the problem"
}

Common Errors

400 - Invalid File Type

{
  "detail": "Type de fichier non supporté. Utilisez: image/jpeg, image/png, image/webp, image/heic, image/heif"
}

Cause: Uploaded file is not a supported image format.

Solution: Ensure your image is JPEG, PNG, WebP, or HEIC format.

# Check file type before uploading
import mimetypes

mime_type, _ = mimetypes.guess_type("plant.jpg")
if mime_type not in ["image/jpeg", "image/png", "image/webp", "image/heic"]:
    print(f"Unsupported file type: {mime_type}")

400 - File Too Large

{
  "detail": "Image trop volumineuse. Maximum: 10MB"
}

Cause: Image file exceeds 10MB limit.

Solution: Compress or resize the image before uploading.

from PIL import Image
import io

def compress_image(image_path, max_size_mb=10):
    """Compress image to fit under max_size_mb."""
    img = Image.open(image_path)

    # Convert to RGB if needed
    if img.mode in ("RGBA", "P"):
        img = img.convert("RGB")

    # Reduce quality until under limit
    quality = 95
    while True:
        buffer = io.BytesIO()
        img.save(buffer, format="JPEG", quality=quality)
        size_mb = buffer.tell() / (1024 * 1024)

        if size_mb <= max_size_mb or quality <= 10:
            buffer.seek(0)
            return buffer

        quality -= 10

422 - Validation Error

{
  "detail": [
    {
      "loc": ["body", "language"],
      "msg": "value is not a valid enumeration member",
      "type": "type_error.enum"
    }
  ]
}

Cause: Invalid parameter value.

Solution: Check parameter values match the expected types and enums.

# Valid enum values
VALID_LANGUAGES = ["en", "fr", "sw", "es", "pt", "it"]
VALID_DETAIL_LEVELS = ["simple", "standard", "expert"]

# Validate before sending
if language not in VALID_LANGUAGES:
    raise ValueError(f"Invalid language. Use one of: {VALID_LANGUAGES}")

429 - Rate Limit Exceeded

{
  "detail": "Rate limit exceeded"
}

Headers:

Retry-After: 30
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706123456

Cause: More than 30 requests in 1 minute from your IP.

Solution: Implement retry logic with exponential backoff.

import time
import requests

def diagnose_with_retry(url, files, data, max_retries=3):
    """Make request with automatic retry on rate limit."""
    for attempt in range(max_retries):
        response = requests.post(url, files=files, data=data)

        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 30))
            print(f"Rate limited. Waiting {retry_after}s...")
            time.sleep(retry_after)
            continue

        return response

    raise Exception("Max retries exceeded")

500 - Internal Server Error

{
  "detail": "An internal error occurred during diagnosis"
}

Cause: Server-side error (AI processing failed, etc.).

Solution: 1. Retry the request once 2. If persistent, try a different image 3. Contact support with the request_id

def diagnose_with_fallback(url, files, data):
    """Handle server errors gracefully."""
    response = requests.post(url, files=files, data=data)

    if response.status_code == 500:
        # Get request ID for support
        request_id = response.headers.get("X-Request-ID", "unknown")
        print(f"Server error. Request ID: {request_id}")

        # Retry once
        time.sleep(2)
        response = requests.post(url, files=files, data=data)

    return response

Analysis Failures (200 OK with error)

Sometimes the API returns 200 OK but couldn't complete analysis:

{
  "request_id": "...",
  "is_plant": false,
  "crop_health": "unknown",
  "diagnoses": [],
  "error": "Unable to analyze: image does not contain a plant"
}

Always check these fields:

response = requests.post(url, files=files, data=data)
result = response.json()

# Check for analysis issues
if result.get("error"):
    print(f"Analysis error: {result['error']}")

if not result.get("is_plant"):
    print("Image does not contain a plant")

if result.get("image_quality_issue"):
    print(f"Quality issue: {result['image_quality_issue']}")

if result.get("crop_health") == "unknown":
    print("Could not determine plant health")

Complete Error Handling Example

import requests
import time

class TajiriVisionClient:
    def __init__(self, base_url="https://api.tajirifarm.com"):
        self.base_url = base_url

    def diagnose(self, image_path, **kwargs):
        """
        Diagnose plant disease with full error handling.

        Returns:
            dict: Diagnosis result or None on failure
        """
        url = f"{self.base_url}/diagnosis/"

        # Prepare request
        with open(image_path, "rb") as f:
            files = {"image": (image_path, f, "image/jpeg")}
            data = {
                "crop_type": kwargs.get("crop_type"),
                "region": kwargs.get("region"),
                "language": kwargs.get("language", "en"),
                "detail_level": kwargs.get("detail_level", "standard"),
            }

            # Make request with retry
            for attempt in range(3):
                try:
                    response = requests.post(url, files=files, data=data, timeout=60)
                except requests.exceptions.Timeout:
                    print("Request timed out, retrying...")
                    continue
                except requests.exceptions.ConnectionError:
                    print("Connection error, retrying...")
                    time.sleep(2)
                    continue

                # Handle response
                if response.status_code == 200:
                    result = response.json()

                    # Check for soft errors
                    if result.get("error"):
                        print(f"Analysis error: {result['error']}")
                        return None

                    return result

                elif response.status_code == 429:
                    retry_after = int(response.headers.get("Retry-After", 30))
                    print(f"Rate limited, waiting {retry_after}s...")
                    time.sleep(retry_after)

                elif response.status_code == 400:
                    print(f"Bad request: {response.json().get('detail')}")
                    return None

                elif response.status_code >= 500:
                    print(f"Server error, retrying...")
                    time.sleep(2)

            print("Max retries exceeded")
            return None


# Usage
client = TajiriVisionClient()
result = client.diagnose(
    "plant.jpg",
    crop_type="tomato",
    region="Kenya",
    language="en"
)

if result:
    print(f"Diagnosis: {result['diagnoses'][0]['name']}")