BloopAI/vibe-kanban · error
Fallback response for "${table}" is not an object
Error message
Fallback response for "${table}" is not an object What it means
extractFallbackRows validates the JSON payload returned by a table's fallback endpoint: it must be an object, because the row array is looked up under the payload[table] key. This error is thrown when the fallback HTTP endpoint succeeded (2xx) but its body is not a JSON object (e.g. null, an array, a string, or invalid shape parsed as a primitive).
Source
Thrown at packages/web-core/src/shared/lib/electric/collections.ts:412
for (const row of rows) {
syncParams.write({
type: 'insert',
value: row,
metadata: {},
});
}
syncParams.commit();
syncParams.markReady();
}
function extractFallbackRows(
payload: unknown,
table: string
): Array<ElectricRow> {
if (!payload || typeof payload !== 'object') {
throw new Error(`Fallback response for "${table}" is not an object`);
}
const rows = (payload as Record<string, unknown>)[table];
if (!Array.isArray(rows)) {
throw new Error(`Fallback response missing "${table}" array`);
}
return rows as Array<ElectricRow>;
}
async function parseResponseError(
response: Response,
fallbackMessage: string
): Promise<string> {
try {
const body = (await response.json()) as {
message?: string;
error?: string;View on GitHub (pinned to 4deb7eca8f)
Solutions
- Make the fallback endpoint return { "<table>": [ ...rows ] } keyed by the shape's table name.
- Verify the `table` value in the ShapeDefinition matches the key the server uses in the response.
- Log the actual payload on failure to see what the endpoint returned.
- Check for proxies/middleware rewriting successful responses (auth walls returning HTML with 200).
Example fix
// before (server)
res.json(rows);
// after (server)
res.json({ tasks: rows }); // key must equal the shape's table name Defensive patterns
Strategy: type-guard
Validate before calling
const res = await fetch(fallbackUrl);
const payload = await res.json();
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
throw new Error('Fallback endpoint must return an object keyed by table name');
} Type guard
function isFallbackPayload(payload: unknown, table: string): payload is Record<string, ElectricRow[]> {
return (
typeof payload === 'object' && payload !== null && !Array.isArray(payload) &&
Array.isArray((payload as Record<string, unknown>)[table])
);
} Try / catch
try {
const payload = await response.json();
if (!isFallbackPayload(payload, table)) {
console.error('Unexpected fallback payload', payload);
return;
}
} catch (e) {
reportSyncError(e);
} Prevention
- Contract-test every fallback endpoint's response shape
- Keep the response key identical to the shape's table name
- Never return bare arrays, null, or HTML with 200 from fallback routes
- Log raw payloads when adding new fallback sources
When it happens
Trigger: Fallback endpoint returns a bare JSON array instead of { [table]: [...] }; endpoint returns null; endpoint returns plain text/HTML with 200; response.json() parses to a primitive; a proxy intercepts and returns a non-standard body.
Common situations: Backend route returning `res.json(rows)` instead of keyed object; API gateway/error page served with 200; mismatch between the shape's `table` name and the key the server actually uses (e.g. table 'tasks' but server returns { items: [...] }); server upgrade changed response envelope.
Related errors
- Fallback response missing "${table}" array
- Failed to fetch fallback ${args.shape.table} (or server-prov
- Failed to create ${mutation.name} (or server-provided messag
- Failed to update ${mutation.name}: missing key
- Failed to list projects (${res.status})
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/a2dc478b3e7de544.
Report an issue: GitHub.