You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(network): support cloudsync failures.{apply,check} response shape
Replaces the old single `lastFailure` extraction with per-stage
`failures.apply` / `failures.check`. `send.lastFailure` is now sourced
from `failures.apply`; a new `receive.lastFailure` surfaces
`failures.check`. Per-function scoping is strict: send_changes is
send/apply-scoped, check_changes is check-scoped, sync reports both.
Also handles the new dual-shape `/check` response: HTTP 200 with
`{url}` (artifact) vs HTTP 202 with the SyncStatusResponse snapshot
(no artifact yet). Previously the 202 path errored with
"missing 'url'" against the new server.
Bumps CLOUDSYNC_VERSION to 1.0.18 and updates API.md / CHANGELOG.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: API.md
+15-9Lines changed: 15 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -490,6 +490,7 @@ The sync functions follow a consistent error-handling contract:
490
490
|**Endpoint/network errors** (server unreachable, auth failure, bad URL) | SQL error — the function could not execute. |
491
491
|**Apply errors** (`cloudsync_payload_apply` failures — unknown schema hash, invalid checksum, decompression error) | Structured JSON — a `receive.error` string field is included in the response. |
492
492
|**Server-reported apply job failures** (the server processed the request but its own apply job failed) | Structured JSON — a `send.lastFailure` object is included in the response. |
493
+
|**Server-reported check job failures** (the server failed to encode a changeset for the client) | Structured JSON — a `receive.lastFailure` object is included in the response. |
493
494
494
495
This means: if you get JSON back, the server was reachable and the network protocol ran. If you get a SQL error, connectivity or configuration is broken.
495
496
@@ -510,7 +511,7 @@ This means: if you get JSON back, the server was reachable and the network proto
510
511
-`send.status`: The current sync state — `"synced"` (all changes confirmed), `"syncing"` (changes sent but not yet confirmed), `"out-of-sync"` (local changes pending or gaps detected), or `"error"`.
511
512
-`send.localVersion`: The latest local database version.
512
513
-`send.serverVersion`: The latest version confirmed by the server.
513
-
-`send.lastFailure` (optional): Present only when the server reports a failed apply job. The object is forwarded verbatim from the serverand typically includes `jobId`, `code`, `message`, `retryable`, and `failedAt`. It is emitted regardless of `status` so callers can detect server-side failures during `"syncing"` or even after the state has nominally recovered.
514
+
-`send.lastFailure` (optional): Present only when the server reports a failed apply job. Forwarded verbatim from the server's `failures.apply`and typically includes `jobId`, `code`, `stage`, `message`, `retryable`, and `failedAt`. It is emitted regardless of `status` so callers can detect server-side failures during `"syncing"` or even after the state has nominally recovered. This function is **send/apply-scoped**: server-reported check-job failures (`failures.check`) are not surfaced here — see [`cloudsync_network_check_changes()`](#cloudsync_network_check_changes) and [`cloudsync_network_sync()`](#cloudsync_network_sync).
-- With a server-reported failure (e.g. unknown schema hash on the server side):
522
-
-- '{"send":{"status":"out-of-sync","localVersion":1,"serverVersion":0,"lastFailure":{"jobId":44961,"code":"internal_error","message":"cloudsync operation failed: Cannot apply the received payload because the schema hash is unknown 4288148391734624266.","retryable":true,"failedAt":"2026-04-15T22:21:09.018606Z"}}}'
523
+
-- '{"send":{"status":"out-of-sync","localVersion":1,"serverVersion":0,"lastFailure":{"jobId":44961,"code":"internal_error","stage":"apply_payload","message":"cloudsync operation failed: Cannot apply the received payload because the schema hash is unknown 4288148391734624266.","retryable":true,"failedAt":"2026-04-15T22:21:09.018606Z"}}}'
523
524
```
524
525
525
526
---
@@ -533,28 +534,32 @@ If a package of new changes is already available for the local site, the server
533
534
This function is designed to be called periodically to keep the local database in sync.
534
535
To force an update and wait for changes (with a timeout), use [`cloudsync_network_sync(wait_ms, max_retries)`].
535
536
536
-
If the network is misconfigured or the remote server is unreachable, the function raises a SQL error. If the received payload cannot be applied locally (for example because of an unknown schema hash), the error is returned as a `receive.error` field in the JSON response.
537
+
If the network is misconfigured or the remote server is unreachable, the function raises a SQL error. If the received payload cannot be applied locally (for example because of an unknown schema hash), the error is returned as a `receive.error` field in the JSON response. If the server reports an unresolved failed check job (e.g. an `encode_changes` failure), that failure is forwarded as a `receive.lastFailure` object.
537
538
538
539
**Parameters:** None.
539
540
540
541
**Returns:** A JSON string with the receive result:
541
542
542
543
```json
543
-
{"receive": {"rows": N, "tables": ["table1", "table2"], "error": "..."}}
544
+
{"receive": {"rows": N, "tables": ["table1", "table2"], "error": "...", "lastFailure": {...}}}
544
545
```
545
546
546
547
-`receive.rows`: The number of rows received and applied to the local database. `0` when the receive phase failed.
547
548
-`receive.tables`: An array of table names that received changes. Empty (`[]`) if no changes were applied or the receive phase failed.
548
-
-`receive.error` (optional): Present when `cloudsync_payload_apply` failed. Contains a human-readable error message describing why the received payload could not be applied.
549
+
-`receive.error` (optional, string): Present when client-side `cloudsync_payload_apply` failed. Contains a human-readable error message describing why the received payload could not be applied.
550
+
-`receive.lastFailure` (optional, object): Present only when the server reports a failed check job. Forwarded verbatim from the server's `failures.check` and typically includes `jobId`, `dbVersion`, `seq`, `code`, `stage`, `message`, `retryable`, and `failedAt`. Distinct from `receive.error`: `receive.error` describes a client-side apply failure (string), while `receive.lastFailure` describes a server-side check-job failure (object). Both can coexist in the same response. This function is **check-scoped**: server-reported apply-job failures (`failures.apply`) are not surfaced here — see [`cloudsync_network_send_changes()`](#cloudsync_network_send_changes) and [`cloudsync_network_sync()`](#cloudsync_network_sync).
549
551
550
552
**Example:**
551
553
552
554
```sql
553
555
SELECT cloudsync_network_check_changes();
554
556
-- '{"receive":{"rows":3,"tables":["tasks"]}}'
555
557
556
-
-- With an apply error:
558
+
-- With a client-side apply error:
557
559
-- '{"receive":{"rows":0,"tables":[],"error":"Cannot apply the received payload because the schema hash is unknown 7218827471400075525."}}'
"send": {"status": "synced|syncing|out-of-sync|error", "localVersion": N, "serverVersion": N, "lastFailure": {...}},
579
-
"receive": {"rows": N, "tables": ["table1", "table2"], "error": "..."}
584
+
"receive": {"rows": N, "tables": ["table1", "table2"], "error": "...", "lastFailure": {...}}
580
585
}
581
586
```
582
587
583
588
-`send.status`: The current sync state — `"synced"`, `"syncing"`, `"out-of-sync"`, or `"error"`.
584
589
-`send.localVersion`: The latest local database version.
585
590
-`send.serverVersion`: The latest version confirmed by the server.
586
-
-`send.lastFailure` (optional): Same semantics as in [`cloudsync_network_send_changes()`](#cloudsync_network_send_changes) — forwarded verbatim from the server whenever a failed apply job is reported, regardless of `status`.
591
+
-`send.lastFailure` (optional): Same semantics as in [`cloudsync_network_send_changes()`](#cloudsync_network_send_changes) — forwarded verbatim from the server's `failures.apply` whenever a failed apply job is reported, regardless of `status`.
587
592
-`receive.rows`: The number of rows received and applied during the check phase. `0` when the receive phase failed.
588
593
-`receive.tables`: An array of table names that received changes. Empty (`[]`) if no changes were applied or the receive phase failed.
589
-
-`receive.error` (optional): Present when `cloudsync_payload_apply` failed (for example `"Cannot apply the received payload because the schema hash is unknown 7218827471400075525."`). The send result is always preserved so the caller can tell that local changes reached the server even when applying incoming changes failed. The retry loop breaks immediately on apply errors, since failures like schema-hash mismatches do not heal across retries. Endpoint/network errors during the receive phase raise a SQL error instead.
594
+
-`receive.error` (optional, string): Present when client-side `cloudsync_payload_apply` failed (for example `"Cannot apply the received payload because the schema hash is unknown 7218827471400075525."`). The send result is always preserved so the caller can tell that local changes reached the server even when applying incoming changes failed. The retry loop breaks immediately on apply errors, since failures like schema-hash mismatches do not heal across retries. Endpoint/network errors during the receive phase raise a SQL error instead.
595
+
-`receive.lastFailure` (optional, object): Same semantics as in [`cloudsync_network_check_changes()`](#cloudsync_network_check_changes) — forwarded verbatim from the server's `failures.check` whenever a failed check job is reported. Distinct from `receive.error`. `cloudsync_network_sync()` reports both `send.lastFailure` and `receive.lastFailure` when present.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+11Lines changed: 11 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
4
4
5
5
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
7
+
## [1.0.18] - 2026-04-27
8
+
9
+
### Changed
10
+
11
+
-**`cloudsync_network_send_changes()` and `cloudsync_network_sync()`**: `send.lastFailure` is now sourced from the server's per-stage `failures.apply` object. The wire-format SQL field (`send.lastFailure`) is unchanged in shape — it is still forwarded verbatim from the server.
12
+
-**`cloudsync_network_check_changes()`**: now correctly handles the `/check` endpoint's status snapshot response (returned when no artifact is yet available). Previously this path errored with `missing 'url' in check response`; the function now treats it as "no rows yet, not an error" and surfaces any server-reported check failure via the new `receive.lastFailure` field.
13
+
14
+
### Added
15
+
16
+
-**`receive.lastFailure`** JSON field on `cloudsync_network_check_changes()` and `cloudsync_network_sync()`. Forwarded verbatim from the server's `failures.check` (e.g. `encode_changes` job failures), and emitted alongside but distinct from `receive.error` (which remains a string for client-side `cloudsync_payload_apply` failures). Per-function scoping is strict: `cloudsync_network_send_changes()` is send/apply-scoped (only `send.lastFailure`); `cloudsync_network_check_changes()` is check-scoped (only `receive.lastFailure`); `cloudsync_network_sync()` reports both.
0 commit comments