{"record":{"id":"1c4ec8a565742cfa","repo":"BloopAI/vibe-kanban","slug":"failed-to-bulk-update-project-statuses","errorCode":null,"errorMessage":"Failed to bulk update project statuses","messagePattern":"Failed to bulk update project statuses","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/web-core/src/shared/lib/remoteApi.ts","lineNumber":156,"sourceCode":"}\n\nexport interface BulkUpdateProjectStatusItem {\n  id: string;\n  changes: Partial<UpdateProjectStatusRequest>;\n}\n\nexport async function bulkUpdateProjectStatuses(\n  updates: BulkUpdateProjectStatusItem[]\n): Promise<void> {\n  const response = await makeRequest('/v1/project_statuses/bulk', {\n    method: 'POST',\n    body: JSON.stringify({\n      updates: updates.map((u) => ({ id: u.id, ...u.changes })),\n    }),\n  });\n  if (!response.ok) {\n    const error = await response.json();\n    throw new Error(error.message || 'Failed to bulk update project statuses');\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Relay host API functions (served by remote backend)\n// ---------------------------------------------------------------------------\n\nexport async function listRelayHosts(): Promise<RelayHost[]> {\n  const response = await makeRequest('/v1/hosts', { method: 'GET' });\n  if (!response.ok) {\n    throw await parseErrorResponse(response, 'Failed to list relay hosts');\n  }\n\n  const body = (await response.json()) as ListRelayHostsResponse;\n  return body.hosts;\n}\n\n// ---------------------------------------------------------------------------","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/packages/web-core/src/shared/lib/remoteApi.ts#L138-L174","documentation":"bulkUpdateProjectStatuses persists a batch of project status transitions via the remote API. A non-ok response causes it to throw Error(serverMessage || 'Failed to bulk update project statuses'). Invoked by persistStatusChanges, so failures there propagate into the status persistence flow.","triggerScenarios":"Non-ok HTTP from the bulk-update-project-statuses endpoint: invalid status transition, unknown project id, expired auth, server error, or an error body without `message`.","commonSituations":"Status enum changed between frontend/backend versions (client sends a status the server rejects); concurrent edits where the project was deleted; offline/self-hosted gateway returning non-JSON errors.","solutions":["Inspect the thrown error.message and response status for the exact rejection","Verify the status values match the current API enum (regenerate shared/remote-types if the API changed)","Re-fetch projects and retry after a 401/refresh of auth","Make persistStatusChanges catch and surface the error instead of letting it bubble silently"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const validStatuses = new Set(['planning','in_progress','review','done']);\nif (updates.some(u => !validStatuses.has(Object.values(u.changes)[0] as string))) throw new Error('invalid status value');","typeGuard":"function isStatusUpdate(u: BulkUpdateProjectStatusItem): boolean { return typeof u.id === 'string' && Object.keys(u.changes).length > 0; }","tryCatchPattern":"try {\n  await bulkUpdateProjectStatuses(updates);\n} catch (e) {\n  console.error('status persist failed:', e);\n  notify.error(e instanceof Error ? e.message : 'Failed to persist statuses');\n}","preventionTips":["Regenerate shared/remote-types after any backend status-enum change","Catch errors inside persistStatusChanges rather than letting them bubble to unhandled rejections","Verify status transitions client-side against allowed transitions before sending"],"tags":["http","bulk-update","status"],"backgroundTag":"http-request-failed","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}