{"record":{"id":"861a2d1113b9e155","repo":"different-ai/openwork","slug":"fallback-response-status","errorCode":null,"errorMessage":"${fallback} (${response.status}).","messagePattern":"\\$\\{fallback\\} \\(\\$\\{response\\.status\\}\\)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/workflow-data.tsx","lineNumber":29,"sourceCode":"\nexport type WorkflowDraft = {\n  name: string;\n  description?: string;\n  code: string;\n  exampleInput?: unknown;\n  inputSchema?: unknown;\n  outputSchema?: unknown;\n  requiredCapabilities: WorkflowCapability[];\n};\n\nconst keys = {\n  detail: (id: string, maxAgeMs: number) => [\"workflow\", id, \"detail\", maxAgeMs] as const,\n  snapshots: (id: string) => [\"workflow\", id, \"snapshots\"] as const,\n};\n\nasync function checkedRequest(path: string, init: RequestInit, fallback: string) {\n  const { response, payload } = await requestJson(path, init, 170_000);\n  if (!response.ok) throw new Error(getErrorMessage(payload, `${fallback} (${response.status}).`));\n  return payload;\n}\n\nexport function useWorkflowDetail(configObjectId: string, maxAgeMs: number) {\n  return useQuery({\n    queryKey: keys.detail(configObjectId, maxAgeMs),\n    queryFn: async () => {\n      const payload = await checkedRequest(\n      `/v1/workflows/${encodeURIComponent(configObjectId)}?maxAgeMs=${maxAgeMs}`,\n      { method: \"GET\" },\n      \"Failed to load Workflow\",\n      );\n      if (typeof payload !== \"object\" || payload === null || !(\"script\" in payload)) throw new Error(\"The Workflow response was invalid.\");\n      return workflowDetailSchema.parse(payload.script);\n    },\n    enabled: Boolean(configObjectId),\n  });\n}","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/workflow-data.tsx#L11-L47","documentation":"checkedRequest is workflow-data.tsx's shared wrapper around requestJson: if response.ok is false it throws new Error(getErrorMessage(payload, `${fallback} (${response.status}).`)). Every workflow hook (detail, test, save version, run, delete snapshot, update automation) funnels through it, so the fallback string (e.g. 'Failed to load Workflow') plus status identifies which call failed.","triggerScenarios":"Any workflow API call returning non-2xx: GET detail/snapshots 404 (unknown configObjectId), 401/403 auth, 409 conflict on save, 5xx on run/test; long-running test/run timing out at the 170s requestJson budget with a gateway 504.","commonSituations":"Workflow deleted elsewhere while the dashboard holds a stale id; permissions changed; server restart during a 170s test run; API deploy causing transient 5xx.","solutions":["Read response.status in the thrown message: 404 → the workflow id no longer exists, refresh the list; 401/403 → re-auth or request access.","For test/run timeouts, check whether the gateway (not the workflow) returned 504 and raise the gateway timeout or shorten the run.","Retry transient 5xx; invalidate the workflow query keys afterward.","Confirm the configObjectId passed to the hook matches an existing workflow."],"exampleFix":"// before\nif (!response.ok) throw new Error(getErrorMessage(payload, `${fallback} (${response.status}).`));\n// after\nif (!response.ok) {\n  if (response.status >= 500) throw new RetryableError(getErrorMessage(payload, `${fallback} (${response.status}).`));\n  throw new Error(getErrorMessage(payload, `${fallback} (${response.status}).`));\n}","handlingStrategy":"try-catch","validationCode":"function assertWorkflowId(id: string): boolean {\n  return typeof id === \"string\" && id.length > 0;\n}","typeGuard":"function isApiErrorPayload(v: unknown): v is { error?: string; message?: string } {\n  return isRecord(v) && (typeof v.error === \"string\" || typeof v.message === \"string\");\n}","tryCatchPattern":"try {\n  await runWorkflow(id);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : \"\";\n  if (msg.includes(\"(404)\")) await invalidateAndRefreshList();\n  else if (msg.includes(\"(401)\") || msg.includes(\"(403)\")) promptAuth();\n  else if (msg.includes(\"(504)\")) showTimeoutNotice();\n  else throw e;\n}","preventionTips":["Refresh workflow ids from the list endpoint rather than long-lived local state","Configure gateway timeouts above the 170s requestJson budget for test/run calls","Classify retryable (5xx) vs permanent (4xx) errors in query retry logic"],"tags":["http","api","network"],"backgroundTag":"http-request-failed","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}