Error Handling

Link to section

Overview

The Reporting API uses standard HTTP status codes and returns structured error responses. Understanding error patterns and implementing proper retry logic is essential for building resilient applications.

Link to section

Common first-time issues

If you're just getting started and hitting errors, here are the most frequent issues and quick fixes:

403 Forbidden — Your access token is invalid or missing.

  1. Verify your token is correct and hasn't expired
  2. Check the header format: Authorization: Bearer YOUR_TOKEN
  3. Ensure you're using a personal access token or an OAuth token with REPORTING_READ scope

400 Bad Request — "Cube not found" — The cube or view name in your query doesn't exist.

  1. Fetch available names via GET /v1/meta
  2. Names are case-sensitive — use them exactly as shown in metadata
  3. Views (like Sales) are often easier to start with than cubes

Empty results ("data": []) — Your query succeeded but matched no data.

  1. Verify your date range includes actual transaction data
  2. Try a broader date range to confirm data exists
  3. Make sure you're using the right view/cube for your use case

"Continue wait" never resolves — Your query is too complex or the date range is too large.

  1. Reduce the date range
  2. Remove unnecessary dimensions
  3. Simplify to fewer measures
  4. See Continue wait pattern below for proper retry logic

For detailed explanations, see the specific sections below.

Link to section

HTTP status codes

Status CodeMeaningCommon Causes
200SuccessQuery executed successfully or "Continue wait"
400Bad RequestInvalid query structure, unknown measures/dimensions
401UnauthorizedMissing authentication token
403ForbiddenInvalid or expired token, insufficient permissions
404Not FoundInvalid endpoint path
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error, database timeout
503Service UnavailableService temporarily unavailable
Link to section

Error response structure

All errors return a JSON object with an error field:

{ "error": "Error message" }
Link to section

Example Error Responses

Link to section

Invalid Measure

{ "error": "Measure 'Orders.invalid_measure' not found" }
Link to section

Invalid Authentication

{ "error": "Unauthorized" }
Link to section

Database Timeout

{ "error": "Query execution timeout" }
Link to section

Continue wait pattern

The most common "error" you'll encounter isn't actually an error—it's the Continue wait response.

Link to section

What is Continue Wait?

When a query takes time to process, the API returns:

{ "error": "Continue wait" }

HTTP Status: 200 (Success)

This is not an error. It means:

  • Your query is valid
  • Processing is underway
  • You should retry the same request
Link to section

Why Continue Wait Exists

Complex queries may need to:

  • Scan large amounts of data
  • Perform multiple aggregations
  • Wait for pre-aggregation builds

Rather than holding the connection open, the API returns quickly and asks you to poll.

Link to section

Continue Wait Flow

reporting-api-error-handling-continue-wait

Link to section

Implementing Continue Wait Retry

Link to section

Python Example

Link to section

JavaScript Example

Link to section

Continue Wait Best Practices

  1. Use exponential backoff: Start with 2s, increase to 5s, then 10s
  2. Set reasonable max attempts: 10-20 attempts is typical
  3. Log retry attempts: Help with debugging
  4. Don't retry forever: Fail gracefully after max attempts
  5. Keep the same query: Don't modify the query between retries
Link to section

Exponential Backoff Example

Link to section

Validation errors

Validation errors occur when your query references invalid entities or has structural issues.

Link to section

Common Validation Errors

Link to section

Unknown Measure

Error:

{ "error": "Measure 'Orders.unknown_measure' not found" }

Cause: The measure doesn't exist in the current schema.

Solution:

  1. Fetch fresh metadata: GET /v1/meta
  2. Verify the measure name (case-sensitive)
  3. Check if the measure was deprecated

Prevention:

def validate_measures(query, metadata): """Validate measures before querying.""" valid_measures = set() for cube in metadata['cubes']: valid_measures.update(m['name'] for m in cube['measures']) for measure in query.get('measures', []): if measure not in valid_measures: raise ValueError(f"Invalid measure: {measure}")
Link to section

Unknown Dimension

Error:

{ "error": "Dimension 'Orders.unknown_dimension' not found" }

Solution: Same as unknown measure—validate against metadata.

Link to section

Unknown Segment

Error:

{ "error": "Segment 'Orders.unknown_segment' not found" }

Solution: Check available segments in metadata.

Link to section

Invalid Date Range

Error:

{ "error": "Invalid date range format" }

Cause: Date format is incorrect.

Solution: Use YYYY-MM-DD format:

{ "dateRange": ["2024-01-01", "2024-01-31"] }
Link to section

Missing Required Fields

Error:

{ "error": "Time dimension must include 'dimension' and 'dateRange'" }

Solution: Ensure time dimensions are complete:

{ "timeDimensions": [{ "dimension": "Orders.sale_timestamp", "dateRange": ["2024-01-01", "2024-01-31"] }] }
Link to section

Authentication errors

Link to section

401 Unauthorized

Error:

{ "error": "Unauthorized" }

Causes:

  • Missing Authorization header
  • Malformed header (not Bearer TOKEN)
  • Empty token

Solution:

# Correct format Authorization: Bearer YOUR_ACCESS_TOKEN # Wrong formats Authorization: YOUR_ACCESS_TOKEN # Missing "Bearer" Authorization: Bearer # Missing token
Link to section

403 Forbidden

Error:

{ "error": "Forbidden" }

Causes:

  • Invalid access token
  • Expired token
  • Token lacks required permissions
  • Using OAuth token without REPORTING_READ scope

Solution:

  1. Verify token in Developer Console
  2. Generate a new personal access token
  3. Check token hasn't expired
  4. Ensure you're using a personal access token (not OAuth)
Link to section

Rate limiting

Link to section

429 Too Many Requests

Error:

{ "error": "Rate limit exceeded" }

Solution: Implement exponential backoff with Retry-After header:

Link to section

Rate Limit Best Practices

  1. Cache results: Don't re-query for the same data
  2. Batch queries: Combine multiple measures in one query
  3. Use appropriate cache strategies: Leverage server-side caching
  4. Respect Retry-After: Don't retry immediately
  5. Monitor usage: Track query volume
Link to section

Server errors

Link to section

500 Internal Server Error

Error:

{ "error": "Internal server error" }

Causes:

  • Database timeout
  • Server-side bug
  • Resource exhaustion

Solution:

  1. Retry with exponential backoff (may be transient)
  2. Simplify the query (reduce date range, dimensions)
  3. Contact support if persistent
Link to section

503 Service Unavailable

Error:

{ "error": "Service temporarily unavailable" }

Cause: Service is down for maintenance or experiencing issues.

Solution:

  1. Retry with exponential backoff
  2. Check Square status page
  3. Implement circuit breaker pattern
Link to section

Query timeout errors

Link to section

Database Timeout

Error:

{ "error": "Query execution timeout" }

Causes:

  • Query is too complex
  • Date range is too large
  • Too many dimensions
  • No pre-aggregations available

Solutions:

Link to section

1. Reduce Date Range

// Instead of {"dateRange": ["2020-01-01", "2024-12-31"]} // Try {"dateRange": ["2024-01-01", "2024-01-31"]}
Link to section

2. Reduce Dimensions

// Instead of {"dimensions": ["Orders.location_id", "Orders.customer_id", "Orders.device_id"]} // Try {"dimensions": ["Orders.location_id"]}
Link to section

3. Increase Granularity

// Instead of {"granularity": "day"} // Try {"granularity": "month"}
Link to section

4. Add Filters

{ "filters": [{ "member": "Orders.location_id", "operator": "equals", "values": ["L1234567890ABC"] }] }
Link to section

Empty result errors

Link to section

No Data Returned

Response:

{ "data": [] }

This is not an error, but you should handle it:

results = execute_query(query) if not results.get('data'): print("No data found for the specified criteria") # Handle empty case else: print(f"Found {len(results['data'])} rows")

Common causes:

  • Date range has no transactions
  • Filters are too restrictive
  • Segment excludes all data
  • Wrong cube for your use case
Link to section

Schema evolution errors

Link to section

Deprecated Measure

Error:

{ "error": "Measure 'Orders.legacy_measure' is deprecated. Use 'Orders.new_measure' instead." }

Solution:

  1. Update your query to use the new measure
  2. Invalidate your metadata cache
  3. Re-fetch metadata to discover new measures
Link to section

Complete error handling example

Here's a production-ready error handling implementation:

Link to section

Monitoring and logging

Link to section

What to Log

Link to section

Metrics to Track

  • Query success rate
  • Average query duration
  • Continue wait frequency
  • Timeout frequency
  • Rate limit hits
  • Error types and frequency
Link to section

Troubleshooting checklist

When a query fails:

  1. Check HTTP status code

    • 401/403: Authentication issue
    • 400: Invalid query
    • 429: Rate limit
    • 500/503: Server issue
  2. Verify authentication

    • Token is valid
    • Header format is correct
    • Using personal access token (not OAuth)
  3. Validate query structure

    • All measures exist in metadata
    • All dimensions exist in metadata
    • Date ranges are valid
    • Required fields are present
  4. Simplify the query

    • Reduce date range
    • Remove dimensions
    • Increase granularity
    • Add filters
  5. Check for Continue wait

    • Implement retry logic
    • Use exponential backoff
    • Set reasonable max attempts
  6. Review metadata

    • Refresh metadata cache
    • Check for deprecated measures
    • Verify cube names