Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,14 @@ def get_non_retryable_errors(self) -> dict[str, str | None]:
**missing_scope_errors,
"missing or invalid refresh token": "Your HubSpot connection is invalid or expired. Please reconnect it.",
"missing or unknown hub id": None,
# HubSpot's CRM API returns 401/403 when the OAuth grant can't read the requested object
# (token revoked, or the connected app is missing a scope like `crm.objects.companies.read`).
# `fetch_data` already refreshes the access token once on a 401; if the retried request is
# still rejected, the credentials genuinely lack access and retrying can't recover. Match the
# stable host, not the per-object URL path (companies/deals/contacts/...), which varies.
"401 Client Error: Unauthorized for url: https://api.hubapi.com": "Your HubSpot credentials are no longer authorized. Please reconnect your HubSpot account and ensure it has the required permissions, then try again.",
# A 401 means the OAuth grant can't read the requested object (token revoked, or the
# connected app lost a scope like `crm.objects.companies.read`). Every fetch loop
# (fetch_data._get, fetch_page, v4 associations, search) refreshes the access token on a
# 401 and re-raises HubspotRetryableError, so tenacity retries with a fresh token. Five
# straight 401s after a good refresh means the grant is dead, not a transient blip, so
# retrying can't recover. Match the shared message fragment all four loops emit, not the
# per-loop prefix or the volatile URL.
"401 - refreshed token, retrying": "Your HubSpot credentials are no longer authorized. Please reconnect your HubSpot account and ensure it has the required permissions, then try again.",
"403 Client Error: Forbidden for url: https://api.hubapi.com": "Your HubSpot credentials do not have permission to access this data. Please reconnect your HubSpot account and ensure it has the required permissions, then try again.",
# Raised by source_for_pipeline when the source config carries no refresh token at all
# (integration never connected or lost its token). Retrying cannot recover.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,13 @@ def test_source_for_pipeline_threads_resolved_version(self, pin: str | None, exp
@pytest.mark.parametrize(
"error_msg",
[
# Raised by fetch_data when a token refresh succeeds but the retried request is still rejected
"401 Client Error: Unauthorized for url: https://api.hubapi.com/crm/v3/properties/companies",
"401 Client Error: Unauthorized for url: https://api.hubapi.com/crm/v3/properties/deals",
# Each fetch loop refreshes the token on a 401 and re-raises this after tenacity's 5 attempts
"Hubspot API 401 - refreshed token, retrying: url=https://api.hubapi.com/crm/v3/properties/companies",
"Hubspot API 401 - refreshed token, retrying: url=https://api.hubapi.com/crm/v3/objects/deals",
"Hubspot v4 associations 401 - refreshed token, retrying: "
"url=https://api.hubapi.com/crm/v4/associations/contacts/deals/batch/read",
"Hubspot search 401 - refreshed token, retrying: url=https://api.hubapi.com/crm/v3/objects/contacts/search",
# raise_for_hubspot_status maps a 403 to this verbatim
"403 Client Error: Forbidden for url: https://api.hubapi.com/crm/v3/objects/contacts",
],
)
Expand Down
Loading