{"record":{"id":"f82892102fe28083","repo":"theonedev/onedev","slug":"tool-not-found-toolname","errorCode":null,"errorMessage":"Tool not found: ${toolName}","messagePattern":"Tool not found: (.+?)","errorType":"exception","errorClass":"ExplicitException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/ai/DefaultChatService.java","lineNumber":327,"sourceCode":"\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tvar aiMessage = completeResponse.aiMessage();\n\t\t\t\t\t\t\t\tif (aiMessage.hasToolExecutionRequests()) {\n\t\t\t\t\t\t\t\t\tlangchain4jMessages.add(aiMessage);\n\t\t\t\t\t\t\t\t\tvar toolRequests = aiMessage.toolExecutionRequests();\n\t\t\t\t\t\t\t\t\tvar connectionRegistry = WebSocketSettings.Holder.get(application).getConnectionRegistry();\n\t\t\t\t\t\t\t\t\tvar connection = connectionRegistry.getConnection(application, sessionId, pageKey);\n\t\t\t\t\t\t\t\t\tif (connection == null || !connection.isOpen())\n\t\t\t\t\t\t\t\t\t\tthrow new ExplicitException(\"Conversation context lost\");\n\t\t\t\t\t\t\t\t\tfor (var toolRequest: toolRequests) {\n\t\t\t\t\t\t\t\t\t\tif (responseFuture.isDone())\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\tString toolName = toolRequest.name();\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tvar toolExecution = new AiToolExecution(toolName, ToolUtils.getToolArguments(toolRequest));\n\t\t\t\t\t\t\t\t\t\t\tconnection.sendMessage(toolExecution);\n\t\t\t\t\t\t\t\t\t\t\tvar toolExecutionFuture = toolExecution.getFuture();\n\t\t\t\t\t\t\t\t\t\t\tif (toolExecutionFuture == null)\n\t\t\t\t\t\t\t\t\t\t\t\tthrow new ExplicitException(\"Tool not found: \" + toolName);\n\t\t\t\t\t\t\t\t\t\t\twhile (true) {\n\t\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\t\tvar toolExecutionResult = toolExecutionFuture.get(1, TimeUnit.SECONDS);\n\t\t\t\t\t\t\t\t\t\t\t\t\ttoolExecutionResult.addToMessages(langchain4jMessages, toolRequest);\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\t} catch (TimeoutException e) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (responseFuture.isDone())\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t\t\t} catch (CancellationException e) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t// may get cancelled in DefaultManagedFutureService\n\t\t\t\t\t\t\t\t\t\t\t\t\tthrow new ExplicitException(\"Timed out calling tool: \" + toolName);\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\t\t\t\t\t\t\tToolUtils.handleCallException(toolName, t);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/ai/DefaultChatService.java#L309-L345","documentation":"After forwarding a tool call to the client, the service obtains an AiToolExecution future used to collect the tool's result. A null future means no registered handler/tool matched the requested tool name, so the call can never complete and the service fails fast with this error.","triggerScenarios":"The LLM emitted a toolExecutionRequest whose name does not correspond to any tool registered/executable for this conversation, so toolExecution.getFuture() returns null in onCompleteResponse.","commonSituations":"Model hallucinating a tool name not in the tool spec; tool registry changed between model training/context and current server version; custom tools not registered on the server; typo or casing mismatch in tool definitions.","solutions":["Verify the tool is registered server-side (check tool definitions exposed to the model) and matches the name casing the model received","Update OneDev / AI tool definitions so the model's tool list matches available handlers","Clear the chat and start a new conversation so the model re-fetches the current tool specs","If you added custom tools, confirm they are contributed before the chat session starts"],"exampleFix":"// before: model requests 'listAllFiles' which is not registered\n// after: constrain the model to registered tools / validate names before sending\nif (ToolUtils.findTool(toolName) == null) {\n    logger.warn(\"Model requested unknown tool: \" + toolName);\n    return; // or send a tool-error result instead of throwing\n}","handlingStrategy":"validation","validationCode":"// before relying on a tool, confirm it is registered/exposed to the model\nboolean known = availableToolNames.stream().anyMatch(n -> n.equals(toolName));\nif (!known) throw new IllegalArgumentException(\"Unknown tool: \" + toolName);","typeGuard":null,"tryCatchPattern":"try {\n    chatService.chat(chatId, prompt);\n} catch (ExplicitException e) {\n    if (e.getMessage().startsWith(\"Tool not found: \")) {\n        String tool = e.getMessage().substring(\"Tool not found: \".length());\n        // refresh tool specs / start new conversation\n    }\n}","preventionTips":["Keep client-side tool definitions in sync with server-registered tools","Start a fresh conversation after upgrading OneDev or changing tools","Validate model-emitted tool names against the registered tool list","Log unknown tool requests to spot model hallucinations"],"tags":["ai-tools","tool-registry","llm"],"backgroundTag":"resource-not-found","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}