debuggingapierrorsdeveloper-guideproduction

Debugging 422 Errors from Body Measurement APIs in Production

· 6 min read · Martin Hejda

HTTP 422 Unprocessable Entity means the server understood your request format but couldn’t process it because the data was semantically invalid. For body measurement APIs, this is overwhelmingly the most common error class in production — and the most fixable.

A 422 is not a random failure. It means you sent something specific that the API couldn’t work with. Here’s how to find and fix it.


The 422 error response structure

A well-designed API returns a structured error body alongside the 422 status code. Parse it:

import requests

def call_prediction_api(payload: dict) -> dict:
    response = requests.post(
        "https://dimensionspot-bodysize-engine.p.rapidapi.com/v1/predict",
        json=payload,
        headers={
            "X-RapidAPI-Key": "YOUR_API_KEY",
            "X-RapidAPI-Host": "dimensionspot-bodysize-engine.p.rapidapi.com"
        }
    )
    
    if response.status_code == 422:
        error_body = response.json()
        # FastAPI/Pydantic validation errors come back as:
        # {"detail": [{"loc": ["body", "input_data", "anchors", "body_height"], 
        #              "msg": "value is not a valid integer", "type": "type_error.integer"}]}
        detail = error_body.get("detail", [])
        if isinstance(detail, list):
            for error in detail:
                loc = " → ".join(str(x) for x in error.get("loc", []))
                msg = error.get("msg", "unknown error")
                print(f"Validation error at {loc}: {msg}")
        else:
            print(f"API error: {detail}")
        raise ValueError(f"422 Unprocessable Entity: {error_body}")
    
    response.raise_for_status()
    return response.json()

The #1 cause: body_height in cm instead of mm

This is the single most common 422-producing mistake. The API expects body_height in millimeters. If you pass centimeters, the value will be in a range that looks like a height in cm (say, 168) but as mm, 168mm = 16.8cm — a physiologically impossible height that the API rejects.

The bug:

# WRONG
"anchors": {
    "body_height": 168,    # This is cm! Should be 1680mm
    "body_mass": 62
}

The fix:

# CORRECT
height_cm = 168
"anchors": {
    "body_height": int(height_cm * 10),  # 1680mm — always multiply by 10
    "body_mass": 62
}

Detection: If you’re seeing 422s and the body_height value in your request is between 100 and 250, you’re sending centimeters. Multiply by 10.


The #2 cause: invalid gender value

The API accepts lowercase "male" and "female". Common variants that trigger 422:

# THESE WILL FAIL:
"gender": "Male"      # Capitalized
"gender": "MALE"      # All caps
"gender": "m"         # Single letter
"gender": "M"         # Single letter capitalized
"gender": "man"       # Non-standard
"gender": 0           # Integer

# CORRECT:
"gender": "male"
"gender": "female"

Add a normalization step at your input boundary:

def normalize_gender(raw_gender: str) -> str:
    """Normalize gender input to API-expected format."""
    normalized = raw_gender.strip().lower()
    
    if normalized in ("male", "m", "man", "masculine", "1"):
        return "male"
    elif normalized in ("female", "f", "woman", "feminine", "0"):
        return "female"
    else:
        raise ValueError(f"Unrecognized gender value: '{raw_gender}'. Expected 'male' or 'female'.")

The #3 cause: body_mass out of range

The API has minimum and maximum values for body_mass. Passing a weight below the minimum (e.g., 0, or a nonsensical value like weight_kg = 5) or above the maximum will trigger 422.

Common causes:

  • Weight entered in pounds, not kg: 140 lbs → 63.5 kg is fine; but if someone enters 140 as “kg”, 140kg is at the upper end but valid; if they enter 300 lbs → 136kg, that’s fine; but 400 lbs → 181kg could be at the limit
  • Data pipeline bug where weight was accidentally set to 0 or None
  • Child weight entered with adult validator (a 5-year-old weighing 18kg is fine for pediatric; invalid for adult)
def validate_weight_before_api(
    weight_kg: float,
    age_category: str = "ADULT"
) -> float:
    """
    Validate weight is within API's acceptable range for the age category.
    Raises ValueError with a specific message if invalid.
    """
    MIN_WEIGHT = {
        "INFANT": 2.0,
        "TODDLER": 8.0,
        "CHILD": 12.0,
        "PRE_TEEN": 20.0,
        "TEEN": 30.0,
        "ADULT": 30.0
    }
    MAX_WEIGHT = {
        "INFANT": 15.0,
        "TODDLER": 25.0,
        "CHILD": 60.0,
        "PRE_TEEN": 90.0,
        "TEEN": 150.0,
        "ADULT": 300.0
    }
    
    min_w = MIN_WEIGHT.get(age_category, 30.0)
    max_w = MAX_WEIGHT.get(age_category, 300.0)
    
    if weight_kg < min_w:
        raise ValueError(
            f"body_mass {weight_kg}kg is below minimum {min_w}kg for age_category={age_category}. "
            f"Check units — are you using lbs? Convert: weight_kg = weight_lbs * 0.453592"
        )
    
    if weight_kg > max_w:
        raise ValueError(
            f"body_mass {weight_kg}kg exceeds maximum {max_w}kg for age_category={age_category}."
        )
    
    return weight_kg

The #4 cause: invalid region or calculation model

Region and calculation model values are enumerations. Sending a value not in the allowed set triggers 422:

# VALID region values:
VALID_REGIONS = {
    "GLOBAL", "EUROPE", "ASIA_PACIFIC", "AFRICA", "LATAM", "INDIA", "MIDDLE_EAST"
}

# VALID age_category values:
VALID_AGE_CATEGORIES = {
    "ADULT", "INFANT", "TODDLER", "CHILD", "PRE_TEEN", "TEEN"
}

# VALID calculation_model values:
VALID_CALCULATION_MODELS = {"AUTO", "ADULT", "PEDIATRIC"}

# INVALID examples:
"input_origin_region": "US"          # Not in the enum
"input_origin_region": "eu"          # Lowercase
"age_category": "KID"                # Not in the enum
"calculation_model": "adult"         # Lowercase

The #5 cause: pediatric + anchor combination mismatch

The pediatric model (INFANT, TODDLER, CHILD, PRE_TEEN, TEEN) interprets anchors differently. Some anchor combinations that are valid for adults are invalid or produce errors for pediatric:

# ADULT: body_height is stature (standing)
# PEDIATRIC INFANT (<24 months): expects recumbent length
# The API may reject body_height values that imply an impossible adult stature
# for a given age_category

# Safe pattern for pediatric:
{
    "input_data": {
        "subject": {
            "gender": "female",
            "age_category": "CHILD",  # Not ADULT
            "input_origin_region": "GLOBAL"
        },
        "anchors": {
            "body_height": 1100,  # 110cm — reasonable for a 6-year-old
            "body_mass": 19.5
        }
    }
}

Production 422 monitoring

Log every 422 with enough context to diagnose the cause:

import logging
import json

logger = logging.getLogger("body_measurement_api")

def monitored_api_call(
    gender: str,
    height_mm: int,
    weight_kg: float,
    region: str,
    age_category: str = "ADULT"
) -> dict:
    payload = {
        "input_data": {
            "input_unit_system": "metric",
            "subject": {"gender": gender, "input_origin_region": region, "age_category": age_category},
            "anchors": {"body_height": height_mm, "body_mass": weight_kg}
        },
        "output_settings": {
            "calculation": {"target_region": region, "body_build_type": "CIVILIAN"},
            "requested_dimensions": {"bundle": "TORSO"}
        }
    }
    
    response = requests.post(
        "https://dimensionspot-bodysize-engine.p.rapidapi.com/v1/predict",
        json=payload,
        headers={
            "X-RapidAPI-Key": "YOUR_API_KEY",
            "X-RapidAPI-Host": "dimensionspot-bodysize-engine.p.rapidapi.com"
        },
        timeout=10
    )
    
    if response.status_code == 422:
        # Log with enough context to diagnose
        logger.error(
            "422 from prediction API",
            extra={
                "gender": gender,
                "height_mm": height_mm,
                "height_cm_approx": height_mm / 10,  # Log cm equivalent to spot cm-vs-mm errors
                "weight_kg": weight_kg,
                "region": region,
                "age_category": age_category,
                "error_response": response.text[:500]
            }
        )
        
        # For development: raise with full context
        try:
            error_detail = response.json().get("detail", "No detail")
        except Exception:
            error_detail = response.text
        
        raise ValueError(
            f"422 Unprocessable Entity from prediction API. "
            f"height_mm={height_mm} ({height_mm/10}cm), weight_kg={weight_kg}, "
            f"gender={gender!r}, region={region!r}. "
            f"API detail: {error_detail}"
        )
    
    response.raise_for_status()
    return response.json()

Pre-flight validation: catch 422s before they happen

Run validation before the API call to catch the most common errors locally:

def preflight_validate(
    gender: str,
    height_mm: int,
    weight_kg: float,
    region: str,
    age_category: str = "ADULT"
) -> list[str]:
    """
    Return a list of validation errors. Empty list = safe to call API.
    """
    errors = []
    
    if gender not in ("male", "female"):
        errors.append(f"Invalid gender: {gender!r}. Must be 'male' or 'female' (lowercase).")
    
    if region not in VALID_REGIONS:
        errors.append(f"Invalid region: {region!r}. Must be one of {VALID_REGIONS}.")
    
    if age_category not in VALID_AGE_CATEGORIES:
        errors.append(f"Invalid age_category: {age_category!r}. Must be one of {VALID_AGE_CATEGORIES}.")
    
    # Height sanity check — most common: cm passed instead of mm
    if height_mm < 500:
        errors.append(
            f"body_height={height_mm} is too small to be in mm. "
            f"Are you passing cm? body_height should be in mm (e.g., 170cm → 1700mm)."
        )
    
    if height_mm > 2600:
        errors.append(f"body_height={height_mm}mm ({height_mm/10}cm) exceeds maximum plausible height.")
    
    if weight_kg <= 0:
        errors.append(f"body_mass={weight_kg} must be positive.")
    
    # BMI sanity check
    if height_mm > 500 and weight_kg > 0:
        height_m = height_mm / 1000
        bmi = weight_kg / (height_m ** 2)
        if bmi < 8 or bmi > 80:
            errors.append(
                f"BMI={bmi:.1f} from height_mm={height_mm}, weight_kg={weight_kg} is implausible. "
                "Check both values."
            )
    
    return errors

Quick 422 debugging checklist

When you hit a 422:

  1. Is body_height in mm? Print height_mm / 10 — if that’s a reasonable height in cm, you’re accidentally sending cm.
  2. Is gender lowercase? "male" and "female", not "Male" or "M".
  3. Is body_mass in kg? Values above 200 may be in lbs. Values below 20 for adults are implausible.
  4. Is the region valid? Must be one of: GLOBAL, EUROPE, ASIA_PACIFIC, AFRICA, LATAM, INDIA, MIDDLE_EAST.
  5. Does the age_category match the height/weight? A CHILD age_category with 180cm height will fail.
  6. Read the error detail field in the 422 response body — the API will usually tell you exactly which field failed and why.

422 errors are a solved problem once you add proper validation upstream. The pre-flight validation function above catches 90%+ of production 422s before they reach the API, and the structured logging pattern ensures you can diagnose the remaining cases from production logs without having to reproduce them locally.

Try DimensionsPot

Free tier — 100 requests/month, no credit card required.

Get API on RapidAPI