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 @@ -116,6 +116,16 @@ def get_non_retryable_errors(self) -> dict[str, str | None]:
return {
"401 Client Error": "Authentication failed. Check your Convex deploy key.",
"403 Client Error": "Access denied. Check your Convex deploy key.",
# A sync only calls list_snapshot / document_deltas, and Convex answers those with a 404
# when the table schema discovery listed is gone at read time (deleted on the source, or a
# component table that isn't served by streaming export). The next scheduled run reissues
# the identical request, so every retry replays the same 404. Cloudflare surfaces transient
# edge problems as the 52x/530 family instead (retried in `_CONVEX_RETRY`), so a 404 is
# never a transient blip that this could disable a sync over.
"404 Client Error": (
"PostHog couldn't find this table in your Convex deployment. It was likely deleted, so "
"turn off syncing for this table, then re-enable the sync."
),
"StreamingExportNotEnabled": "Streaming export requires the Convex Professional plan. See https://www.convex.dev/plans to upgrade.",
# Match a stable substring of the raised message, not the `InvalidWindowError` class name:
# the non-retryable check compares against `str(exception)`, which contains the message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,11 @@ def test_get_json_schemas_400_with_unparseable_body_falls_through_to_http_error(
[
("401", "401 Client Error: Unauthorized for url: https://x.convex.cloud/api/document_deltas"),
("403", "403 Client Error: Forbidden for url: https://x.convex.cloud/api/document_deltas"),
(
"missing_table_404",
"404 Client Error: Not Found for url: "
"https://x.convex.cloud/api/list_snapshot?tableName=verification&format=json&component=betterAuth",
),
(
"invalid_window",
"Delta cursor for table 'events' is older than Convex's ~30 day retention window. "
Expand All @@ -569,6 +574,19 @@ def test_known_errors_match(self, _name: str, observed_error: str) -> None:
non_retryable_errors = ConvexSource().get_non_retryable_errors()
assert any(key in observed_error for key in non_retryable_errors)

def test_missing_table_404_surfaces_actionable_message(self) -> None:
# A deleted table's 404 must stop retrying and tell the customer to turn off syncing for it,
# not store the raw driver text (which carries the deployment host). Mirror the finalizer's
# first-match selection so a reorder that shadowed it with an earlier None key would be caught.
error_msg = (
"404 Client Error: Not Found for url: "
"https://x.convex.cloud/api/list_snapshot?tableName=verification&format=json&component=betterAuth"
)
matches = [friendly for key, friendly in ConvexSource().get_non_retryable_errors().items() if key in error_msg]
assert matches, "a missing-table 404 must be classified non-retryable"
assert matches[0] is not None, "a missing-table 404 must surface an actionable message, not raw driver text"
assert "turn off syncing" in matches[0].lower()

@parameterized.expand(
[
("server_error", "500 Server Error for url: https://x.convex.cloud/api/document_deltas"),
Expand Down
Loading