{"record":{"id":"e7e4a0c826c6b72f","repo":"different-ai/openwork","slug":"desktop-automation-execution-timed-out","errorCode":null,"errorMessage":"Desktop Automation execution timed out","messagePattern":"Desktop Automation execution timed out","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/automation-runner.mjs","lineNumber":179,"sourceCode":"  const workspace = resolveAssignmentWorkspace(listed, assignment.workspaceId ?? null)\n  const workspaceId = String(workspace.id)\n  const client = createWorkspaceSessionClient(local, workspaceId, options.fetchImpl ?? fetch)\n  const created = await client.createThread({\n    title: `Automation: ${assignment.automationName}`.slice(0, 120),\n    ...(assignment.instructions ? { prompt: assignment.instructions } : {}),\n    model: assignment.model,\n    signal: options.signal,\n  })\n  const sessionId = created.id\n  // The assignment signal is already aborted when this listener runs. Do not\n  // pass it to the cleanup request or fetch can reject before reaching OpenCode.\n  const abort = () => void client.abortThread(sessionId).catch(() => undefined)\n  options.signal.addEventListener(\"abort\", abort, { once: true })\n  try {\n    const startedAt = Date.now()\n    const deadlineAt = startedAt + assignment.timeoutMs\n    while (true) {\n      if (Date.now() > deadlineAt) throw new Error(\"Desktop Automation execution timed out\")\n      // The wall-clock check above only runs between awaits. A machine that\n      // suspends mid-request can leave this socket half-open with no error,\n      // which would make the assignment timeout unreachable: bound each poll\n      // by the remaining execution budget so the deadline always fires.\n      let snapshot\n      try {\n        snapshot = await client.getThreadSnapshot(sessionId, {\n          signal: AbortSignal.any([options.signal, AbortSignal.timeout(Math.max(1, deadlineAt - Date.now()))]),\n          limit: 200,\n        })\n      } catch (error) {\n        if (!options.signal.aborted && Date.now() >= deadlineAt) throw new Error(\"Desktop Automation execution timed out\")\n        throw error\n      }\n      const output = assistantResult(snapshot)\n      const snapshotError = assistantFailure(snapshot)\n      if (snapshotError) {\n        const error = new Error(snapshotError.message)","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/automation-runner.mjs#L161-L197","documentation":"The polling loop in executeDesktopAutomation enforces assignment.timeoutMs via a wall-clock deadline checked at the top of every iteration. If the assistant thread has not produced a terminal result before the deadline, execution is abandoned with this timeout error (the thread is aborted via the registered abort listener).","triggerScenarios":"getThreadSnapshot polling looped past startedAt + assignment.timeoutMs without the thread reaching a result — e.g. the agent task legitimately runs longer than the configured timeout, or polling stalls.","commonSituations":"Assignment timeoutMs configured too low for a long-running agent task; agent stuck waiting on user input/permission prompt; slow model responses; machine under heavy load slowing execution.","solutions":["Increase assignment.timeoutMs to cover the expected task duration","Investigate why the thread never went idle/produced a result (check session logs)","Break the task into smaller assignments with shorter scopes","Check for permission prompts blocking the automated thread"],"exampleFix":"// before: default 60s timeout for a long research task\nconst assignment = { prompt: \"deep research...\", timeoutMs: 60_000 };\n// after\nconst assignment = { prompt: \"deep research...\", timeoutMs: 15 * 60_000 };","handlingStrategy":"try-catch","validationCode":"// Ensure the budget covers the task before submitting\nconst estimatedMs = estimateTaskDuration(assignment.prompt);\nif (estimatedMs >= assignment.timeoutMs) {\n  assignment.timeoutMs = Math.max(assignment.timeoutMs, estimatedMs * 2);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const result = await executeDesktopAutomation(assignment, options);\n} catch (err) {\n  if (err.message === \"Desktop Automation execution timed out\") {\n    // thread already aborted; retry with a larger budget\n    await executeDesktopAutomation({ ...assignment, timeoutMs: assignment.timeoutMs * 2 }, options);\n  } else throw err;\n}","preventionTips":["Set timeoutMs generously for long-running agent tasks","Monitor thread progress between polls and log last activity time","Avoid scheduling automations that require interactive permissions","Alert on timeout-rate trends per task type"],"tags":["timeout","automation","polling"],"backgroundTag":"execution-timeout","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}