Skip to Content
TroubleshootingOverview

Troubleshooting

Symptom-first index of the failure modes you are most likely to hit, what each one actually means, and where the fix lives. Every entry is grounded in the platform’s real contracts — status codes, response hints, and dashboard surfaces — not folklore.

The event is accepted (202) but nothing appears in HubSpot

The 202 only means “queued”. Three causes, in order of likelihood:

  1. No published flow subscribes to the event type. Flows match on the exact (source: "event_api", event_type) pair. The send response carries a hint naming the unmatched event types, and GET /api/v1/events/{id}/status reports status: "skipped". Fix the flow’s event_type (or add a wildcard like order.*) and publish.
  2. The flow exists only in the draft. Nothing applies until you go through Review changes → Publish — the draft is never executed. See draft → publish. The send-time hint is computed against the published config, so it catches this case too.
  3. A flow matched but a step failed. Check the per-entity error and step trace in /status — each entity row names the failing step and the classified error.

When the record is written but with unexpected values, use GET /api/v1/events/{id}/verify: it re-reads HubSpot and diffs it property-by-property against what your published flow projects.

401 Unauthorized on /api/v1/events

The x-api-key header is missing, malformed, or the key was revoked. Keys are connector-scoped (ps_live_...); the secret is shown once at creation. Mint a new one from connector → Settings → API keys.

423 Locked on /api/v1/events

The connector is paused or in error: ingestion is deliberately blocked, nothing is queued. Resume the connector from the dashboard. Pause stops both sides — event intake and source polling.

429 Too Many Requests

The org’s monthly event quota for its tier is exhausted. The response detail carries events_used_this_period and period_resets_at. Bulk import requests are additionally rate-limited per org — read the X-RateLimit-* response headers before retrying.

503 Service Unavailable

A transient infrastructure failure (ingest queue or attachment store unreachable). Nothing was enqueued — the request is safe to retry with backoff.

This is the ingest call failing. A transient failure after the event was accepted is retried by the platform on its own schedule, documented with its real numbers in Reliability: retry, failures, replay.

/status reports partial_failure

At least one entity failed, or only part of a multi-entity event synced. Read the per-entity rows in the same response: each carries action, status, hubspot_id, and error detail. partial_failure is also the terminal fallback for an event past the stuck deadline that has at least one entity already reported: the entities that never reported are counted as the gap between entities_total and entities_done.

If the event is past the same deadline but no entity has reported at all, /status reports delayed instead, not partial_failure: that shape means the event is simply still sitting in a slow FIFO backlog (queue depth, other-tenant contention), not a crash. See the hint field for how long it has been queued, and the reference table in GET /api/v1/events/{id}/status for the full status ladder.

A record ended up parked, failed, or quarantined

Those are record-level states in the connector’s sync events, one row per record, and they are distinct from the event-level /status ladder above. The short version:

  • parked - a transient failure (typically a dependency that had not synced yet) used up its 5 delivery attempts without ever writing anything. Not a bug: give it time and replay it.
  • failed - a permanent error, or a mix that includes one. A replay alone will not fix it; fix the config or the payload first.
  • quarantined - an identity_guard rejection: too many identity fields changed at once on an existing record, which usually means a source-side join error. Replay requires an explicit force=true.

Nothing is lost in any of the three cases: the row carries the original payload and a replayable flag, and POST /api/connectors/{id}/sync-events/{event_id}/replay re-runs it against the currently published config, so “fix, publish, replay” works. Full semantics, retry numbers and the record-mode range replay: Reliability: retry, failures, replay.

Syncs stopped on one flow but the rest of the connector works

A 401 / 403 (revoked token, missing OAuth scope, wrong form GUID) is fatal rather than transient: retrying only hammers the target. After 3 consecutive fatal outcomes on the same flow, that flow’s circuit breaker trips and it is skipped while its siblings keep syncing. Fix the credential or the flow, then reopen it with POST /api/connectors/{id}/flows/{flow_name}/breaker/reset. When every enabled flow has tripped, the connector itself moves to error and ingestion starts answering 423; setting it back to active reopens everything at once. See Reliability: retry, failures, replay.

The Conflicts tab is empty and you expected conflicts

Almost always correct behavior, not a broken engine:

  • Under source_wins (the default, including when the conflicts block is absent) no detection runs at all — zero rows is the strategy working.
  • Under target_wins / most_recent, only a property whose last HubSpot edit came from outside plugsync is ever arbitrated. If plugsync is the only writer, every write is conflict-free by definition.

GET /api/connectors/{id}/conflicts/count returns policy_active and never_matched so you can confirm the policy is live without waiting for a real conflict. Full semantics (including the source_modified_path envelope-vs-record trap): conflicts reference.

An outbound flow never fires

Outbound events enter through an event source (HubSpot journal, REST poller, scheduled tick, file), not through the Event API:

  1. Check the source exists and is enabled — a flow whose source names a missing or disabled source silently never matches.
  2. Check the flow’s event_type against what the source actually emits (the HubSpot journal emits per-object change types; a poller emits what its jsonpath extraction names).

See event sources.

Field mapping errors in the dashboard

The mapping editor validates against real captured payloads: every event ingested is sampled, so the editor knows the actual shape of $.payload.*. A mapping that references a path absent from the samples is flagged inline. Send one representative event first, then map — see getting started, step 5.

Draft edits seem to have no effect

By design. All writes — dashboard, AI assistant, MCP, setup scripts — land in the draft; only an explicit human Publish from Review changes promotes them to the workers. If publish returns a 409, someone else published since you started editing: reset your draft or keep editing and re-review. See draft → publish.

Still stuck?

  • The CLI can pull connector status, logs, and configs from the terminal (plugsync --help).
  • Enterprise and Custom-Dedicated orgs: use your dedicated support channel.
  • Anything that looks like a platform bug: contact support from the dashboard.
Last updated on