From hubspot-pack
Diagnose and fix HubSpot API errors like 401, 403, 409 with real JSON responses, causes, and solutions including curl token checks and scope regeneration.
How this skill is triggered — by the user, by Claude, or both
Slash command
/hubspot-pack:hubspot-common-errorsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Quick reference for the most common HubSpot API errors, their real error response bodies, and solutions.
Quick reference for the most common HubSpot API errors, their real error response bodies, and solutions.
@hubspot/api-client installedCheck 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" }
}
]
}
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:
Authorization: Bearer headerFix:
# 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"
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.
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: [] });
}
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
});
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);
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 | -- |
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
);
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');
}
}
# 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
For comprehensive debugging, see hubspot-debug-bundle.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin hubspot-packIdentifies HubSpot API anti-patterns like deprecated auth, missing batch ops, search limits, and wrong associations, with TypeScript fixes for code reviews and audits.
Provides patterns for HubSpot CRM integration: OAuth authentication, CRM objects, associations, batch operations, webhooks, custom objects using Node.js and Python SDKs.
Provides expert patterns for HubSpot CRM integration: OAuth authentication, CRM objects, associations, batch operations, webhooks, and custom objects using Node.js and Python SDKs.