{"record":{"id":"bacabc62473e41a3","repo":"BloopAI/vibe-kanban","slug":"failed-to-fetch-fallback-args-shape-table-or-s","errorCode":null,"errorMessage":"Failed to fetch fallback ${args.shape.table} (or server-provided message via parseResponseError)","messagePattern":"Failed to fetch fallback (.+?) \\(or server-provided message via parseResponseError\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/web-core/src/shared/lib/electric/collections.ts","lineNumber":469,"sourceCode":"\n    const refreshNow = async () => {\n      if (refreshPromise) {\n        return refreshPromise;\n      }\n\n      refreshPromise = (async () => {\n        try {\n          const response = await makeRequest(\n            buildFallbackRequestPath(args.shape.fallbackUrl, args.params),\n            { method: 'GET', cache: 'no-store' }\n          );\n\n          if (!response.ok) {\n            const message = await parseResponseError(\n              response,\n              `Failed to fetch fallback ${args.shape.table}`\n            );\n            throw new Error(message);\n          }\n\n          const payload = (await response.json()) as unknown;\n          const rows = extractFallbackRows(payload, args.shape.table);\n          fallbackSnapshotCache.set(args.sourceKey, rows);\n\n          if (!isCleanedUp) {\n            applySnapshot(syncParams, rows);\n          }\n        } catch (error) {\n          if (isAbortError(error)) return;\n\n          const message =\n            error instanceof Error ? error.message : 'Fallback fetch failed';\n          args.reportError({ message });\n\n          if (!isCleanedUp && !syncParams.collection.isReady()) {\n            syncParams.markReady();","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/packages/web-core/src/shared/lib/electric/collections.ts#L451-L487","documentation":"refreshNow in the fallback sync path fetches the full table snapshot from the fallback endpoint; if the response is not ok it builds a message via parseResponseError (server's `message`/`error` field if parseable, else 'Failed to fetch fallback <table>') and throws. The thrown message is then reported via args.reportError and the collection may be marked ready with empty/partial data, so users may only see this via the sync error callback.","triggerScenarios":"Fallback endpoint returns 401/403 (not authenticated), 404 (fallbackUrl wrong or route missing), 422 (bad params in buildFallbackRequestPath), or 5xx (backend/DB down) during initial fallback sync or a periodic/invalidated refresh.","commonSituations":"Electric unavailable and fallback URL misconfigured in the shape definition; auth cookie expired so fallback GET gets 401; backend deployment removed the plain REST list route the fallback relies on; request params contain characters needing encoding that break the route.","solutions":["Read the reported message: if it's the fallback text, check response status by inspecting the endpoint manually (curl the fallbackUrl with the same params).","Fix auth (re-login) if the underlying status is 401/403 — the fallback fetch shares the session.","Correct args.shape.fallbackUrl / params so buildFallbackRequestPath targets an existing list route.","Since refreshNow swallows the throw into reportError, attach an onElectricUnavailable/reportError handler that surfaces status ≥ 500 to the UI and retries."],"exampleFix":"// before\ncreateCollection({ shape: { url, table: 'tasks', fallbackUrl: '/wrong-path' } });\n// after\ncreateCollection({ shape: { url, table: 'tasks', fallbackUrl: '/api/tasks' },\n  reportError: (e) => showErrorToast(e.message) });","handlingStrategy":"retry","validationCode":"const probe = await makeRequest(buildFallbackRequestPath(shape.fallbackUrl, params), { method: 'HEAD' });\nif (!probe.ok) console.warn('Fallback endpoint unhealthy:', probe.status);","typeGuard":"function isFallbackFetchError(msg: string): boolean {\n  return msg.startsWith('Failed to fetch fallback ');\n}","tryCatchPattern":"createCollection({\n  reportError: (err) => {\n    if (isFallbackFetchError(err.message)) {\n      scheduleRetryWithBackoff();\n      showOfflineBanner();\n    }\n  },\n});","preventionTips":["Verify fallbackUrl routes exist and require only the session auth you have","Refresh login before long sessions so fallback GETs don't 401","Surface reportError messages in the UI instead of swallowing them","Monitor for status ≥ 500 and trigger onElectricUnavailable/reconnect logic"],"tags":["network","electric-sql","fallback-sync","http-error"],"backgroundTag":"api-non-2xx-response","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}