hubspot-common-errors

Installation
SKILL.md

HubSpot Common Errors

Overview

Quick reference for the most common HubSpot API errors, their real error response bodies, and solutions.

Prerequisites

  • @hubspot/api-client installed
  • API credentials configured
  • Access to application logs

Instructions

Step 1: Identify the Error

Check the HTTP status code and response body. HubSpot returns structured errors:

{
  "status": "error",
  "message": "One or more validation errors occurred",
  "correlationId": "abc123-def456",
  "category": "VALIDATION_ERROR",
  "errors": [
    {
      "message": "Property values were not valid: [{\"isValid\":false,\"message\":\"...\"}]",
      "context": { "propertyName": "email" }
    }
  ]
}

Step 2: Match and Fix


401 Unauthorized

Real response:

{
  "status": "error",
  "message": "Authentication credentials not found. This API supports OAuth 2.0 authentication and you can find more details at https://developers.hubspot.com/docs/methods/auth/oauth-overview",
  "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "category": "INVALID_AUTHENTICATION"
}

Causes:

  • Missing Authorization: Bearer header
  • Expired OAuth access token (30-minute TTL)
  • Revoked or regenerated private app token

Fix:

# Verify token is set and valid
curl -s https://api.hubapi.com/crm/v3/objects/contacts?limit=1 \
  -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" | jq .status
# Should return null (success) or "error"

403 Forbidden

Real response:

{
  "status": "error",
  "message": "This access token does not have proper permissions. (requires any of [crm.objects.contacts.write])",
  "correlationId": "...",
  "category": "MISSING_SCOPES"
}

Fix: Add the missing scope to your private app in Settings > Integrations > Private Apps, then regenerate the token.


409 Conflict

Real response:

{
  "status": "error",
  "message": "Contact already exists. Existing ID: 12345",
  "correlationId": "...",
  "category": "CONFLICT"
}

Fix: Search before creating, or use batch upsert:

// Use search-first pattern
const existing = await client.crm.contacts.searchApi.doSearch({
  filterGroups: [{
    filters: [{ propertyName: 'email', operator: 'EQ', value: email }],
  }],
  properties: ['email'],
  limit: 1, after: 0, sorts: [],
});

if (existing.results.length > 0) {
  await client.crm.contacts.basicApi.update(existing.results[0].id, { properties });
} else {
  await client.crm.contacts.basicApi.create({ properties, associations: [] });
}

429 Too Many Requests

Real response:

{
  "status": "error",
  "message": "You have reached your secondly limit.",
  "correlationId": "...",
  "category": "RATE_LIMITS"
}

Headers returned:

X-HubSpot-RateLimit-Daily: 500000
X-HubSpot-RateLimit-Daily-Remaining: 499950
X-HubSpot-RateLimit-Secondly: 10
X-HubSpot-RateLimit-Secondly-Remaining: 0
Retry-After: 1

Fix: Honor Retry-After header and use SDK built-in retries:

const client = new hubspot.Client({
  accessToken: process.env.HUBSPOT_ACCESS_TOKEN!,
  numberOfApiCallRetries: 3, // auto-retries 429s with backoff
});

400 Validation Error (Property)

Real response:

{
  "status": "error",
  "message": "Property values were not valid: [{\"isValid\":false,\"message\":\"Property \\\"nonexistent_field\\\" does not exist\",\"error\":\"PROPERTY_DOESNT_EXIST\",\"name\":\"nonexistent_field\"}]",
  "correlationId": "...",
  "category": "VALIDATION_ERROR"
}

Fix:

// List available properties for an object type
// GET /crm/v3/properties/{objectType}
const properties = await client.crm.properties.coreApi.getAll('contacts');
const propNames = properties.results.map(p => p.name);
console.log('Available contact properties:', propNames);

400 Invalid Association

Real response:

{
  "status": "error",
  "message": "association type id 999 doesn't exist between contact and company",
  "correlationId": "...",
  "category": "VALIDATION_ERROR"
}

Common association type IDs:

From To TypeId Label
Contact Company 1 Primary
Contact Deal 3 --
Company Deal 5 --
Contact Ticket 16 --
Note Contact 202 --
Task Contact 204 --
Note Deal 214 --

404 Not Found (Archived Record)

Real response:

{
  "status": "error",
  "message": "Object not found. objectType=contacts, objectId=12345. It may have been deleted.",
  "correlationId": "...",
  "category": "OBJECT_NOT_FOUND"
}

Fix: The record may be archived. Check with archived=true:

const contact = await client.crm.contacts.basicApi.getById(
  '12345',
  ['email'],
  undefined,
  undefined,
  true // archived = true
);

Output

  • Error identified by HTTP status and category
  • Root cause determined
  • Fix applied with working code
  • Verified resolution

Error Handling

Catch-All Error Handler

import { HttpError } from '@hubspot/api-client/lib/codegen/crm/contacts';

async function handleHubSpotError(error: any): Promise<void> {
  const status = error?.code || error?.statusCode || 500;
  const body = typeof error?.body === 'string' ? JSON.parse(error.body) : error?.body || {};

  console.error(`HubSpot API Error [${status}]`, {
    message: body.message,
    category: body.category,
    correlationId: body.correlationId,
    errors: body.errors,
  });

  // Save correlationId for support tickets
  if (status >= 500) {
    console.error('HubSpot server error. Check https://status.hubspot.com');
  }
}

Examples

Quick Diagnostic Commands

# Check HubSpot API status
curl -s https://status.hubspot.com/api/v2/summary.json | jq '.status.description'

# Verify token and scopes
curl -s https://api.hubapi.com/crm/v3/objects/contacts?limit=1 \
  -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" \
  -w "\nHTTP Status: %{http_code}\n"

# Check rate limit headers
curl -sI https://api.hubapi.com/crm/v3/objects/contacts?limit=1 \
  -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" \
  | grep -i ratelimit

Resources

Next Steps

For comprehensive debugging, see hubspot-debug-bundle.

Weekly Installs
1
GitHub Stars
2.1K
First Seen
Mar 25, 2026