@@ -10,7 +10,12 @@ import type { SyncCursor } from "../cursor.js";
1010import type { Logger } from "../../logger/index.js" ;
1111import type { DataStoragePort } from "../../ports/index.js" ;
1212import { buildDataBlocks } from "../../storage/blocks/build.js" ;
13- import { classifySyncFailure } from "../issues.js" ;
13+ import {
14+ classifySyncFailure ,
15+ inferPayloadKind ,
16+ type SyncFailureStage ,
17+ type SyncPayloadKind ,
18+ } from "../issues.js" ;
1419
1520/** Minimal diagnostics hook — keeps core free of lite-specific imports. */
1621export interface DownloadDiagnosticsHook {
@@ -48,6 +53,13 @@ export interface DownloadResult {
4853 path : string ;
4954}
5055
56+ interface SyncFailureMetadata {
57+ stage ?: SyncFailureStage ;
58+ scope ?: string ;
59+ payloadKind ?: SyncPayloadKind ;
60+ encryptedSizeBytes ?: number ;
61+ }
62+
5163/**
5264 * Download and process a single file record from the storage backend:
5365 * 1. Check dedup: skip if fileId already in local index
@@ -121,9 +133,19 @@ export async function downloadOne(
121133 try {
122134 plaintext = await decryptWithPassword ( encrypted , scopeKeyHex ) ;
123135 } catch ( err ) {
124- const detail = ( err as Error ) . message ;
136+ const payloadKind = inferPayloadKind ( encrypted ) ;
137+ const encryptedSizeBytes = encrypted . byteLength ;
138+ const detail = [
139+ ( err as Error ) . message ,
140+ `payloadKind=${ payloadKind } ` ,
141+ `encryptedSizeBytes=${ encryptedSizeBytes } ` ,
142+ ] . join ( "; " ) ;
125143 diagnostics ?. onDecryptError ( record . fileId , schema . scope , detail ) ;
126- throw err ;
144+ throw withSyncFailureMetadata ( err , {
145+ scope : schema . scope ,
146+ payloadKind,
147+ encryptedSizeBytes,
148+ } ) ;
127149 }
128150
129151 // 6. Parse as DataFileEnvelope (validate)
@@ -267,18 +289,26 @@ export async function downloadAll(
267289 results . push ( result ) ;
268290 }
269291 } catch ( err ) {
292+ const metadata = getSyncFailureMetadata ( err ) ;
270293 const classified = classifySyncFailure ( {
271294 error : err ,
272295 fileId : file . fileId ,
273296 schemaId : file . schemaId ,
274297 syncRunId,
298+ stage : metadata . stage ,
299+ scope : metadata . scope ,
300+ payloadKind : metadata . payloadKind ,
301+ encryptedSizeBytes : metadata . encryptedSizeBytes ,
275302 } ) ;
276303 if ( ! classified . issue . retryable ) {
277304 logger . warn (
278305 {
279306 fileId : file . fileId ,
280307 schemaId : file . schemaId ,
308+ scope : classified . issue . scope ,
281309 stage : classified . issue . stage ,
310+ payloadKind : classified . issue . payloadKind ,
311+ encryptedSizeBytes : classified . issue . encryptedSizeBytes ,
282312 errorClass : classified . issue . errorClass ,
283313 message : classified . issue . message ,
284314 } ,
@@ -372,6 +402,25 @@ function uint8ToHex(bytes: Uint8Array): string {
372402 ) ;
373403}
374404
405+ function withSyncFailureMetadata (
406+ error : unknown ,
407+ metadata : SyncFailureMetadata ,
408+ ) : unknown {
409+ if ( error instanceof Error ) {
410+ Object . assign ( error , { syncFailureMetadata : metadata } ) ;
411+ return error ;
412+ }
413+ return { error, syncFailureMetadata : metadata } ;
414+ }
415+
416+ function getSyncFailureMetadata ( error : unknown ) : SyncFailureMetadata {
417+ if ( typeof error !== "object" || error === null ) return { } ;
418+ const metadata = ( error as { syncFailureMetadata ?: unknown } )
419+ . syncFailureMetadata ;
420+ if ( typeof metadata !== "object" || metadata === null ) return { } ;
421+ return metadata as SyncFailureMetadata ;
422+ }
423+
375424async function writeBlockSidecars (
376425 storage : DataStoragePort ,
377426 envelope : {
0 commit comments