{"record":{"id":"16def35357d8322f","repo":"can1357/oh-my-pi","slug":"lsp-action-timed-out-after-timeoutsec-s-on","errorCode":null,"errorMessage":"LSP ${action} timed out after ${timeoutSec}s on ${serverName}. The server may still be indexing; try again or pass timeout=<larger>.","messagePattern":"LSP (.+?) timed out after (.+?)s on (.+?)\\. The server may still be indexing; try again or pass timeout=<larger>\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/lsp/tool.ts","lineNumber":1525,"sourceCode":"\n\t\t\t\tdefault:\n\t\t\t\t\toutput = `Unknown action: ${action}`;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: output }],\n\t\t\t\tdetails: { serverName, action, success: true, request: params },\n\t\t\t\t...(useless ? { useless: true } : {}),\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof ToolError) throw err;\n\t\t\tif (err instanceof ToolAbortError || signal?.aborted) {\n\t\t\t\t// Distinguish a wall-clock timeout from a caller cancel:\n\t\t\t\t// callerSignal aborting → real cancel (re-throw ToolAbortError);\n\t\t\t\t// timeoutSignal aborting without callerSignal → emit a ToolError naming the\n\t\t\t\t// elapsed budget and server, instead of opaque \"Operation aborted\".\n\t\t\t\tif (timeoutSignal.aborted && !callerSignal?.aborted) {\n\t\t\t\t\tthrow new ToolError(\n\t\t\t\t\t\t`LSP ${action} timed out after ${timeoutSec}s on ${serverName}. The server may still be indexing; try again or pass timeout=<larger>.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new ToolAbortError();\n\t\t\t}\n\t\t\tconst errorMessage = err instanceof Error ? err.message : String(err);\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `LSP error: ${errorMessage}` }],\n\t\t\t\tdetails: { serverName, action, success: false, request: params },\n\t\t\t};\n\t\t}\n\t}\n}\n","sourceCodeStart":1507,"sourceCodeEnd":1539,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/lsp/tool.ts#L1507-L1539","documentation":"When an LSP tool action exceeds its wall-clock budget, the tool converts the generic abort into a descriptive ToolError naming the action, the budget in seconds, and the server, and suggests retrying with a larger timeout. The server itself may still be indexing and remains usable.","triggerScenarios":"execute() awaited a request (symbols, references, definition, etc.) past `timeoutSec` (from the `timeout` param clamped by tools.maxTimeout), the timeoutSignal fired, and the caller signal was not aborted — the catch branch sees ToolAbortError with timeoutSignal.aborted && !callerSignal?.aborted.","commonSituations":"First query after opening a large project while the language server is still indexing; slow servers (jdtls, rust-analyzer on huge workspaces) with the default timeout; symbol/project-wide queries over big codebases.","solutions":["Retry the same call — indexing may have completed in the meantime.","Pass a larger `timeout` value in the tool params (respecting tools.maxTimeout).","Wait for the server to finish indexing (check the `status` action) before issuing expensive queries.","Raise the `tools.maxTimeout` setting if the default clamp is too low for your workflow."],"exampleFix":"// before\nawait lspTool.execute({ action: \"symbols\", query: \"foo\" });\n// after\nawait lspTool.execute({ action: \"symbols\", query: \"foo\", timeout: 120 });","handlingStrategy":"retry","validationCode":"const status = await lspTool.execute({ action: \"status\" });\nif (isIndexing(status)) await waitForIndexing(status); // before expensive queries","typeGuard":null,"tryCatchPattern":"try {\n  return await lspTool.execute({ action, /* ... */ });\n} catch (err) {\n  if (err instanceof ToolError && /timed out after/.test(err.message)) {\n    await Bun.sleep(2000);\n    return lspTool.execute({ action, timeout: (params.timeout ?? 30) * 2, /* ... */ });\n  } throw err;\n}","preventionTips":["Set generous timeouts for first queries after opening a project.","Poll `status` until indexing completes before bulk queries.","Raise tools.maxTimeout for slow servers (jdtls, large Rust workspaces).","Warm up servers with a cheap hover before heavy project-wide queries."],"tags":["lsp","timeout","indexing","tool"],"backgroundTag":"request-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}