Validation
January 15, 202415 min read

Fix Invalid Request Format Errors

Invalid request format errors occur when your API request doesn't match the expected schema. This guide helps you validate requests, fix common formatting issues, and implement proper error handling.

Common Format Errors

Request format requirements are documented in OpenAI's API reference,Anthropic's API docs, and provider-specific documentation.

JSON Syntax Errors
"Expecting property name enclosed in double quotes"

Malformed JSON structure

Missing Required Fields
"Missing required parameter: 'model'"

Required fields not provided

Type Mismatch
"Invalid type for 'temperature': expected number"

Wrong data type for field

Invalid Values
"temperature must be between 0 and 2"

Values outside allowed range

Correct Request Schemas

Each provider has specific schema requirements. Always refer to the latest documentation for accurate schemas.

OpenAI Chat Completion Schema

interface OpenAIChatRequest {
  model: string;                    // Required: "gpt-4", "gpt-3.5-turbo", etc.
  messages: Array<{                 // Required: Array of message objects
    role: "system" | "user" | "assistant" | "function";
    content: string;
    name?: string;                  // Optional: For function messages
    function_call?: {               // Optional: For function calling
      name: string;
      arguments: string;
    };
  }>;
  
  // Optional parameters
  temperature?: number;             // 0-2, default 1
  top_p?: number;                  // 0-1, default 1
  n?: number;                      // Number of completions
  stream?: boolean;                // Stream response
  stop?: string | string[];        // Stop sequences
  max_tokens?: number;             // Max response length
  presence_penalty?: number;       // -2 to 2
  frequency_penalty?: number;      // -2 to 2
  logit_bias?: Record<string, number>;
  user?: string;                   // Unique user identifier
  
  // Function calling
  functions?: Array<{
    name: string;
    description?: string;
    parameters: object;            // JSON Schema
  }>;
  function_call?: "none" | "auto" | { name: string };
  
  // JSON mode
  response_format?: {
    type: "text" | "json_object";
  };
}

// Example valid request
const validRequest: OpenAIChatRequest = {
  model: "gpt-4",
  messages: [
    {
      role: "system",
      content: "You are a helpful assistant."
    },
    {
      role: "user",
      content: "Hello, how are you?"
    }
  ],
  temperature: 0.7,
  max_tokens: 150
};

Anthropic Messages API Schema

interface AnthropicMessageRequest {
  model: string;                    // Required: "claude-3-opus-20240229", etc.
  messages: Array<{                 // Required: Array of messages
    role: "user" | "assistant";
    content: string | Array<{       // Can be string or content blocks
      type: "text" | "image";
      text?: string;                // For text blocks
      source?: {                    // For image blocks
        type: "base64";
        media_type: string;
        data: string;
      };
    }>;
  }>;
  
  max_tokens: number;              // Required: Maximum tokens to generate
  
  // Optional parameters
  system?: string;                 // System prompt
  metadata?: {                     // User metadata
    user_id?: string;
  };
  stop_sequences?: string[];       // Stop generation at these sequences
  temperature?: number;            // 0-1, default 1
  top_p?: number;                 // Nucleus sampling
  top_k?: number;                 // Top-k sampling
  stream?: boolean;               // Stream response
}

// Example valid request
const validAnthropicRequest: AnthropicMessageRequest = {
  model: "claude-3-sonnet-20240229",
  messages: [
    {
      role: "user",
      content: "What's the weather like?"
    }
  ],
  max_tokens: 1000,
  system: "You are a weather assistant.",
  temperature: 0.5
};

Request Validation Implementation

Implement client-side validation to catch errors before making API calls. Use libraries like Zod orJoi for schema validation.

TypeScript/Zod Validation

import { z } from 'zod';

// Define schemas with Zod
const MessageSchema = z.object({
  role: z.enum(['system', 'user', 'assistant', 'function']),
  content: z.string().min(1, 'Content cannot be empty'),
  name: z.string().optional(),
  function_call: z.object({
    name: z.string(),
    arguments: z.string()
  }).optional()
});

const OpenAIRequestSchema = z.object({
  model: z.string().min(1, 'Model is required'),
  messages: z.array(MessageSchema).min(1, 'At least one message required'),
  temperature: z.number().min(0).max(2).optional(),
  top_p: z.number().min(0).max(1).optional(),
  n: z.number().int().positive().optional(),
  stream: z.boolean().optional(),
  stop: z.union([z.string(), z.array(z.string())]).optional(),
  max_tokens: z.number().int().positive().optional(),
  presence_penalty: z.number().min(-2).max(2).optional(),
  frequency_penalty: z.number().min(-2).max(2).optional(),
  logit_bias: z.record(z.string(), z.number()).optional(),
  user: z.string().optional(),
  response_format: z.object({
    type: z.enum(['text', 'json_object'])
  }).optional()
});

// Validation function with detailed errors
export function validateOpenAIRequest(data: unknown): {
  valid: boolean;
  data?: z.infer<typeof OpenAIRequestSchema>;
  errors?: Array<{path: string; message: string}>;
} {
  try {
    const validated = OpenAIRequestSchema.parse(data);
    return { valid: true, data: validated };
  } catch (error) {
    if (error instanceof z.ZodError) {
      const errors = error.errors.map(err => ({
        path: err.path.join('.'),
        message: err.message
      }));
      return { valid: false, errors };
    }
    throw error;
  }
}

// Usage with error handling
async function makeOpenAIRequest(requestData: unknown) {
  // Validate first
  const validation = validateOpenAIRequest(requestData);
  
  if (!validation.valid) {
    console.error('Validation errors:', validation.errors);
    throw new Error(`Invalid request format: ${
      validation.errors?.map(e => `${e.path}: ${e.message}`).join(', ')
    }`);
  }
  
  // Make API call with validated data
  try {
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
      },
      body: JSON.stringify(validation.data)
    });
    
    if (!response.ok) {
      const error = await response.json();
      handleAPIError(error);
    }
    
    return await response.json();
  } catch (error) {
    console.error('API request failed:', error);
    throw error;
  }
}

// Specific error handling
function handleAPIError(error: any) {
  if (error.error?.type === 'invalid_request_error') {
    // Log the specific field that caused the error
    console.error('Invalid request:', error.error.message);
    
    // Common fixes based on error message
    if (error.error.message.includes('model')) {
      console.log('Fix: Check if the model name is correct and you have access');
    } else if (error.error.message.includes('messages')) {
      console.log('Fix: Ensure messages array is properly formatted');
    }
  }
  
  throw new Error(error.error?.message || 'Unknown API error');
}

Python Request Validation

from typing import Dict, List, Any, Optional, Union
from pydantic import BaseModel, Field, validator
import json
from enum import Enum

class MessageRole(str, Enum):
    system = "system"
    user = "user"
    assistant = "assistant"
    function = "function"

class Message(BaseModel):
    role: MessageRole
    content: str = Field(..., min_length=1)
    name: Optional[str] = None
    function_call: Optional[Dict[str, Any]] = None
    
    @validator('content')
    def content_not_empty(cls, v):
        if not v or not v.strip():
            raise ValueError('Message content cannot be empty')
        return v

class OpenAIRequest(BaseModel):
    model: str = Field(..., min_length=1)
    messages: List[Message] = Field(..., min_items=1)
    temperature: Optional[float] = Field(None, ge=0, le=2)
    top_p: Optional[float] = Field(None, ge=0, le=1)
    n: Optional[int] = Field(None, gt=0)
    stream: Optional[bool] = None
    stop: Optional[Union[str, List[str]]] = None
    max_tokens: Optional[int] = Field(None, gt=0)
    presence_penalty: Optional[float] = Field(None, ge=-2, le=2)
    frequency_penalty: Optional[float] = Field(None, ge=-2, le=2)
    logit_bias: Optional[Dict[str, float]] = None
    user: Optional[str] = None
    
    @validator('messages')
    def validate_message_order(cls, messages):
        """Ensure conversation flow makes sense"""
        if not messages:
            return messages
            
        # First message can be system or user
        if messages[0].role not in [MessageRole.system, MessageRole.user]:
            raise ValueError('First message must be from system or user')
        
        # Check for alternating pattern (simplified)
        for i in range(1, len(messages)):
            if messages[i].role == messages[i-1].role == MessageRole.user:
                # Allow multiple user messages but warn
                print("Warning: Multiple consecutive user messages")
        
        return messages
    
    class Config:
        use_enum_values = True
        json_encoders = {
            Enum: lambda x: x.value
        }

class RequestValidator:
    """Validate and fix common request issues"""
    
    @staticmethod
    def validate_openai_request(data: Dict[str, Any]) -> Dict[str, Any]:
        """Validate and clean OpenAI request"""
        
        try:
            # Parse with Pydantic
            request = OpenAIRequest(**data)
            
            # Additional custom validations
            if request.model.startswith("gpt-4") and request.max_tokens and request.max_tokens > 8000:
                print(f"Warning: max_tokens {request.max_tokens} might be too high for {request.model}")
            
            return request.dict(exclude_none=True)
            
        except ValidationError as e:
            # Extract user-friendly error messages
            errors = []
            for error in e.errors():
                field = '.'.join(str(x) for x in error['loc'])
                message = error['msg']
                errors.append(f"{field}: {message}")
            
            raise ValueError(f"Invalid request format: {'; '.join(errors)}")
    
    @staticmethod
    def fix_common_issues(data: Dict[str, Any]) -> Dict[str, Any]:
        """Attempt to fix common formatting issues"""
        
        # Fix common typos in model names
        if 'model' in data:
            model_fixes = {
                'gpt4': 'gpt-4',
                'gpt3.5': 'gpt-3.5-turbo',
                'claude3': 'claude-3-sonnet-20240229',
                'gpt-4-turbo': 'gpt-4-turbo-preview'
            }
            
            if data['model'] in model_fixes:
                data['model'] = model_fixes[data['model']]
                print(f"Fixed model name: {data['model']}")
        
        # Ensure messages is a list
        if 'messages' in data and isinstance(data['messages'], dict):
            data['messages'] = [data['messages']]
        
        # Fix temperature/top_p conflicts
        if data.get('temperature') == 0 and data.get('top_p') is not None:
            del data['top_p']
            print("Removed top_p when temperature is 0")
        
        # Convert string numbers to actual numbers
        for field in ['temperature', 'top_p', 'max_tokens', 'n']:
            if field in data and isinstance(data[field], str):
                try:
                    if field in ['temperature', 'top_p']:
                        data[field] = float(data[field])
                    else:
                        data[field] = int(data[field])
                except ValueError:
                    del data[field]
                    print(f"Removed invalid {field} value")
        
        return data


# Usage example
def make_validated_request(request_data: Dict[str, Any]):
    """Make API request with validation and error handling"""
    
    # First try to fix common issues
    request_data = RequestValidator.fix_common_issues(request_data)
    
    # Validate
    try:
        validated_data = RequestValidator.validate_openai_request(request_data)
    except ValueError as e:
        print(f"Validation error: {e}")
        
        # Attempt recovery for specific errors
        if "messages" in str(e):
            # Provide a default message
            request_data['messages'] = [{
                'role': 'user',
                'content': request_data.get('prompt', 'Hello')
            }]
            validated_data = RequestValidator.validate_openai_request(request_data)
        else:
            raise
    
    # Make API call
    import openai
    
    try:
        response = openai.ChatCompletion.create(**validated_data)
        return response
        
    except openai.error.InvalidRequestError as e:
        # Handle API-level validation errors
        error_msg = str(e)
        
        if "maximum context length" in error_msg:
            # Reduce message history or max_tokens
            print("Reducing request size due to context length")
            if 'max_tokens' in validated_data:
                validated_data['max_tokens'] = min(validated_data['max_tokens'], 1000)
            if len(validated_data['messages']) > 2:
                validated_data['messages'] = validated_data['messages'][-2:]
            
            # Retry with reduced request
            return openai.ChatCompletion.create(**validated_data)
        
        raise


# Test the validator
test_requests = [
    # Valid request
    {
        "model": "gpt-4",
        "messages": [{"role": "user", "content": "Hello"}]
    },
    
    # Missing required field
    {
        "messages": [{"role": "user", "content": "Hello"}]
    },
    
    # Invalid temperature
    {
        "model": "gpt-4",
        "messages": [{"role": "user", "content": "Hello"}],
        "temperature": 3.0  # Too high
    },
    
    # Wrong type
    {
        "model": "gpt-4",
        "messages": "Hello",  # Should be array
        "temperature": "0.7"  # Should be number
    }
]

for i, test_data in enumerate(test_requests):
    print(f"\nTest {i + 1}:")
    try:
        validated = RequestValidator.validate_openai_request(test_data.copy())
        print("✓ Valid request")
    except ValueError as e:
        print(f"✗ {e}")

Debugging Request Format Errors

Request Logger and Debugger

class APIRequestDebugger {
  private enabled: boolean = process.env.NODE_ENV === 'development';
  
  logRequest(
    endpoint: string,
    requestData: any,
    headers: Record<string, string>
  ): void {
    if (!this.enabled) return;
    
    console.group(`🔍 API Request to ${endpoint}`);
    
    // Log formatted JSON
    console.log('📦 Request Body:');
    console.log(JSON.stringify(requestData, null, 2));
    
    // Validate JSON syntax
    try {
      JSON.parse(JSON.stringify(requestData));
      console.log('✅ Valid JSON syntax');
    } catch (e) {
      console.error('❌ Invalid JSON:', e.message);
    }
    
    // Check for common issues
    this.checkCommonIssues(requestData);
    
    // Log headers (hide sensitive data)
    console.log('📋 Headers:');
    const safeHeaders = { ...headers };
    if (safeHeaders.Authorization) {
      safeHeaders.Authorization = safeHeaders.Authorization.substring(0, 20) + '...';
    }
    console.table(safeHeaders);
    
    console.groupEnd();
  }
  
  private checkCommonIssues(data: any): void {
    const issues: string[] = [];
    
    // Check for undefined values
    const checkUndefined = (obj: any, path: string = ''): void => {
      for (const [key, value] of Object.entries(obj)) {
        const currentPath = path ? `${path}.${key}` : key;
        
        if (value === undefined) {
          issues.push(`Undefined value at ${currentPath}`);
        } else if (value === null) {
          issues.push(`Null value at ${currentPath} (might be intentional)`);
        } else if (typeof value === 'object' && !Array.isArray(value)) {
          checkUndefined(value, currentPath);
        }
      }
    };
    
    checkUndefined(data);
    
    // OpenAI specific checks
    if (data.messages && Array.isArray(data.messages)) {
      data.messages.forEach((msg: any, index: number) => {
        if (!msg.role) {
          issues.push(`Message at index ${index} missing 'role'`);
        }
        if (!msg.content && msg.content !== '') {
          issues.push(`Message at index ${index} missing 'content'`);
        }
      });
    }
    
    // Type checks
    if (data.temperature !== undefined && typeof data.temperature !== 'number') {
      issues.push(`'temperature' should be a number, got ${typeof data.temperature}`);
    }
    
    if (data.max_tokens !== undefined && !Number.isInteger(data.max_tokens)) {
      issues.push(`'max_tokens' should be an integer`);
    }
    
    // Log issues
    if (issues.length > 0) {
      console.warn('⚠️  Potential issues found:');
      issues.forEach(issue => console.warn(`  - ${issue}`));
    } else {
      console.log('✅ No obvious issues detected');
    }
  }
  
  logResponse(response: Response, data: any): void {
    if (!this.enabled) return;
    
    console.group(`📨 API Response (Status: ${response.status})`);
    
    if (!response.ok) {
      console.error('❌ Request failed');
      console.log('Error details:', data);
      
      // Parse error message for hints
      if (data.error?.message) {
        this.suggestFixes(data.error.message);
      }
    } else {
      console.log('✅ Request successful');
    }
    
    console.groupEnd();
  }
  
  private suggestFixes(errorMessage: string): void {
    console.log('💡 Suggested fixes:');
    
    const fixes: Record<string, string> = {
      'model': 'Check model name spelling and availability',
      'messages': 'Ensure messages is an array with proper role/content structure',
      'required': 'Add the missing required field to your request',
      'type': 'Check data types - numbers should not be strings',
      'format': 'Validate JSON syntax using a JSON validator',
      'Invalid value': 'Check API documentation for allowed values',
      'exceeds': 'Reduce the value to be within allowed limits'
    };
    
    for (const [keyword, fix] of Object.entries(fixes)) {
      if (errorMessage.toLowerCase().includes(keyword.toLowerCase())) {
        console.log(`  → ${fix}`);
      }
    }
  }
}

// Usage
const debugger = new APIRequestDebugger();

async function makeDebuggedRequest(requestData: any) {
  const endpoint = 'https://api.openai.com/v1/chat/completions';
  const headers = {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
  };
  
  // Log request
  debugger.logRequest(endpoint, requestData, headers);
  
  try {
    const response = await fetch(endpoint, {
      method: 'POST',
      headers,
      body: JSON.stringify(requestData)
    });
    
    const data = await response.json();
    
    // Log response
    debugger.logResponse(response, data);
    
    if (!response.ok) {
      throw new Error(data.error?.message || 'Request failed');
    }
    
    return data;
  } catch (error) {
    console.error('Request error:', error);
    throw error;
  }
}

Common Format Fixes

1. JSON Syntax Issues

❌ Wrong

{
  "model": "gpt-4",
  "messages": [
    {
      "role": "user"
      "content": "Hello"  // Missing comma
    }
  ],
  "temperature": 0.7,  // Trailing comma
}

✅ Correct

{
  "model": "gpt-4",
  "messages": [
    {
      "role": "user",
      "content": "Hello"
    }
  ],
  "temperature": 0.7
}

2. Type Mismatches

❌ Wrong

{
  "temperature": "0.7",     // String instead of number
  "max_tokens": "100",      // String instead of number
  "stream": "true",         // String instead of boolean
  "messages": {             // Object instead of array
    "role": "user",
    "content": "Hello"
  }
}

✅ Correct

{
  "temperature": 0.7,       // Number
  "max_tokens": 100,        // Number
  "stream": true,           // Boolean
  "messages": [{            // Array
    "role": "user",
    "content": "Hello"
  }]
}

3. Missing Required Fields

❌ Wrong

// OpenAI - Missing model
{
  "messages": [{"role": "user", "content": "Hi"}]
}

// Anthropic - Missing max_tokens
{
  "model": "claude-3-sonnet-20240229",
  "messages": [{"role": "user", "content": "Hi"}]
}

✅ Correct

// OpenAI
{
  "model": "gpt-4",
  "messages": [{"role": "user", "content": "Hi"}]
}

// Anthropic
{
  "model": "claude-3-sonnet-20240229",
  "messages": [{"role": "user", "content": "Hi"}],
  "max_tokens": 1000
}

API Validation Middleware

// Express middleware for request validation
import { Request, Response, NextFunction } from 'express';
import Ajv from 'ajv';

const ajv = new Ajv({ allErrors: true });

// Define JSON Schema for OpenAI requests
const openAISchema = {
  type: 'object',
  required: ['model', 'messages'],
  properties: {
    model: { type: 'string', minLength: 1 },
    messages: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'object',
        required: ['role', 'content'],
        properties: {
          role: { enum: ['system', 'user', 'assistant', 'function'] },
          content: { type: 'string' },
          name: { type: 'string' },
          function_call: {
            type: 'object',
            properties: {
              name: { type: 'string' },
              arguments: { type: 'string' }
            }
          }
        }
      }
    },
    temperature: { type: 'number', minimum: 0, maximum: 2 },
    top_p: { type: 'number', minimum: 0, maximum: 1 },
    n: { type: 'integer', minimum: 1 },
    stream: { type: 'boolean' },
    stop: {
      oneOf: [
        { type: 'string' },
        { type: 'array', items: { type: 'string' } }
      ]
    },
    max_tokens: { type: 'integer', minimum: 1 },
    presence_penalty: { type: 'number', minimum: -2, maximum: 2 },
    frequency_penalty: { type: 'number', minimum: -2, maximum: 2 }
  }
};

const validateOpenAI = ajv.compile(openAISchema);

export const validateRequestMiddleware = (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  // Skip validation for non-JSON requests
  if (!req.is('application/json')) {
    return res.status(415).json({
      error: 'Content-Type must be application/json'
    });
  }
  
  // Validate based on endpoint
  const path = req.path;
  let validate;
  
  if (path.includes('openai') || path.includes('chat/completions')) {
    validate = validateOpenAI;
  } else {
    // Add other validators as needed
    return next();
  }
  
  const valid = validate(req.body);
  
  if (!valid) {
    // Format validation errors
    const errors = validate.errors?.map(err => ({
      field: err.instancePath || err.schemaPath,
      message: err.message,
      params: err.params
    }));
    
    return res.status(400).json({
      error: 'Invalid request format',
      details: errors,
      suggestion: getErrorSuggestion(errors?.[0])
    });
  }
  
  // Add cleaned/validated data to request
  req.body = ajv.compile(openAISchema)(req.body);
  next();
};

function getErrorSuggestion(error: any): string {
  if (!error) return 'Check your request format against the API documentation';
  
  const field = error.field;
  const message = error.message;
  
  // Provide specific suggestions
  if (field.includes('model')) {
    return 'Ensure model name is correct (e.g., "gpt-4", "gpt-3.5-turbo")';
  }
  
  if (field.includes('messages')) {
    return 'Messages must be an array of {role, content} objects';
  }
  
  if (message.includes('required')) {
    return `Add the missing required field: ${field}`;
  }
  
  if (message.includes('type')) {
    return `Check the data type for ${field}`;
  }
  
  return message;
}

Best Practices

JSON Validator

Online tool to validate JSON syntax.

Try validator →

API Reference

Official API documentation.

View docs →

Schema Libraries

Validation tools for your stack.

Explore Zod →

References

  1. [1] OpenAI. "Error Codes Reference" (2024)
  2. [2] Anthropic. "API Errors" (2024)
  3. [3] Stack Overflow. "OpenAI API Questions" (2024)