@@ -530,6 +530,10 @@ export function useForkSync(params: {
530530 mapping . isPlaceholderData ||
531531 ! diff . data ||
532532 diff . isPlaceholderData
533+ // A failed fetch also gates Sync: a failed REFETCH keeps the last successful payload in
534+ // `data` (so `dataPending` stays false), and every gate below would be judging that stale
535+ // snapshot while the page shows the load error.
536+ const dataError = mapping . isError || diff . isError
533537 // Zero-blockers invariant (mirrors the server gate): Sync stays disabled while ANY reference
534538 // would clear in a synced target workflow. `requiredComplete` covers the mapping entries
535539 // (credentials/secrets and unresolved resource refs); `blockingRefs` additionally covers
@@ -541,21 +545,25 @@ export function useForkSync(params: {
541545 ! requiredComplete ||
542546 ! reconfigComplete ||
543547 syncBlocked ||
544- dataPending
545-
546- // Priority mirrors the resolution flow: clear the blockers, map the required resources,
547- // reconfigure their dependents - each failing gate names ITS obstacle (an unmapped
548- // credential/secret is a required-mapping failure, not a cleared-ref blocker; see
549- // `pendingRequiredKinds`).
550- const syncDisabledReason = syncBlocked
551- ? 'Resolve every blocking reference first — map it, copy it, or fix it in the source'
552- : ! requiredComplete
553- ? `Map all required ${ forkRequiredKindsLabel ( pendingRequiredKinds ) } first`
554- : ! reconfigComplete
555- ? 'Reconfigure all required fields first'
556- : dataPending
557- ? 'Loading sync details…'
558- : undefined
548+ dataPending ||
549+ dataError
550+
551+ // A load failure outranks the gates - they're computed from the stale (or absent) payload,
552+ // so naming one would mislead. Then priority mirrors the resolution flow: clear the
553+ // blockers, map the required resources, reconfigure their dependents - each failing gate
554+ // names ITS obstacle (an unmapped credential/secret is a required-mapping failure, not a
555+ // cleared-ref blocker; see `pendingRequiredKinds`).
556+ const syncDisabledReason = dataError
557+ ? "Couldn't load sync details — reload the page to retry"
558+ : syncBlocked
559+ ? 'Resolve every blocking reference first — map it, copy it, or fix it in the source'
560+ : ! requiredComplete
561+ ? `Map all required ${ forkRequiredKindsLabel ( pendingRequiredKinds ) } first`
562+ : ! reconfigComplete
563+ ? 'Reconfigure all required fields first'
564+ : dataPending
565+ ? 'Loading sync details…'
566+ : undefined
559567
560568 const workflowChanges = useMemo < ForkWorkflowChange [ ] > ( ( ) => {
561569 const order : Record < ForkWorkflowChange [ 'action' ] , number > = { update : 0 , create : 1 , archive : 2 }
0 commit comments