{"record":{"id":"9e48e37966b2008c","repo":"alibaba/spring-ai-alibaba","slug":"tool-not-tested","errorCode":"TOOL_NOT_TESTED","errorMessage":"TOOL_NOT_TESTED","messagePattern":"TOOL_NOT_TESTED","errorType":"error_code","errorClass":"BizException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/base/service/impl/PluginServiceImpl.java","lineNumber":583,"sourceCode":"\t\tString key = getToolCacheKey(context.getWorkspaceId(), toolId);\n\t\tredisManager.put(key, entity);\n\t}\n\n\t/**\n\t * Publishes a tool after testing\n\t * @param toolId Tool ID\n\t */\n\t@Override\n\tpublic void publishTool(String toolId) {\n\t\tRequestContext context = RequestContextHolder.getRequestContext();\n\n\t\tToolEntity entity = getToolById(context.getWorkspaceId(), toolId);\n\t\tif (entity == null) {\n\t\t\tthrow new BizException(ErrorCode.TOOL_NOT_FOUND.toError());\n\t\t}\n\n\t\tif (entity.getTestStatus() != ToolTestStatus.PASSED) {\n\t\t\tthrow new BizException(ErrorCode.TOOL_NOT_TESTED.toError());\n\t\t}\n\n\t\tentity.setStatus(ToolStatus.PUBLISHED);\n\t\tentity.setEnabled(true);\n\t\ttoolMapper.updateById(entity);\n\n\t\t// update cache\n\t\tString key = getToolCacheKey(context.getWorkspaceId(), toolId);\n\t\tredisManager.put(key, entity);\n\t}\n\n\t/**\n\t * Retrieves a plugin entity from cache or database\n\t * @param workspaceId Workspace ID\n\t * @param pluginId Plugin ID\n\t * @return Plugin entity or null if not found\n\t */\n\tprivate PluginEntity getPluginById(String workspaceId, String pluginId) {","sourceCodeStart":565,"sourceCodeEnd":601,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/base/service/impl/PluginServiceImpl.java#L565-L601","documentation":"TOOL_NOT_TESTED is thrown by PluginServiceImpl.publishTool when the tool exists but its ToolTestStatus is not PASSED. The library enforces that only tools whose latest test run passed may be published, to prevent broken tool definitions from going live.","triggerScenarios":"Calling publishTool(toolId) on a tool that has never been tested, whose testStatus is FAILED or PENDING, or that was modified after its last successful test so its recorded status is stale.","commonSituations":"Developer publishes immediately after creating/editing a tool without running the test step in the studio UI; a test run failed silently upstream; tool definition edited after passing tests, resetting test status.","solutions":["Run the tool test for this toolId and ensure it passes before publishing.","Check entity.getTestStatus() via the tool detail API; only publish when it is ToolTestStatus.PASSED.","If the tool was edited after testing, re-run the test to refresh the status.","If status is stale due to a bug in test bookkeeping, re-save/re-test the tool to update the persisted state."],"exampleFix":"// before\npluginService.publishTool(toolId); // throws if testStatus != PASSED\n// after\nToolEntity tool = pluginService.getToolById(workspaceId, toolId);\nif (tool == null || tool.getTestStatus() != ToolTestStatus.PASSED) {\n    pluginService.testTool(toolId); // run test first\n}\npluginService.publishTool(toolId);","handlingStrategy":"validation","validationCode":"ToolEntity tool = pluginService.getToolById(workspaceId, toolId);\nif (tool == null || tool.getTestStatus() != ToolTestStatus.PASSED) {\n    throw new IllegalStateException(\"tool must pass tests before publishing\");\n}","typeGuard":"boolean isPublishable(ToolEntity tool) {\n    return tool != null && tool.getTestStatus() == ToolTestStatus.PASSED;\n}","tryCatchPattern":"try {\n    pluginService.publishTool(toolId);\n} catch (BizException e) {\n    if (ErrorCode.TOOL_NOT_TESTED.name().equals(e.getCode())) {\n        // trigger a test run, then retry publish once status is PASSED\n    }\n    throw e;\n}","preventionTips":["Make 'run test' a required workflow step before the publish action is enabled.","Re-run tests automatically after every tool definition edit.","Surface testStatus in the UI next to the publish action.","Gate publish behind a status check in client code."],"tags":["plugin","tool","validation","precondition"],"backgroundTag":"invalid-state-transition","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}