{"record":{"id":"928753bf30d59612","repo":"OtterMind/Chat2DB","slug":"routine-operation-typeunsupported","errorCode":"routine.operation.typeUnsupported","errorMessage":"routine.operation.typeUnsupported","messagePattern":"routine\\.operation\\.typeUnsupported","errorType":"error_code","errorClass":"BusinessException","httpStatus":null,"severity":"error","filePath":"chat2db-community-server/chat2db-community-plugins/chat2db-community-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlRoutineManager.java","lineNumber":328,"sourceCode":"                        .toList();\n            }\n\n            return metaData.getProcedureParameters(connection, databaseName, StringUtils.trimToNull(operation.getSchemaName()),\n                            routineName)\n                    .stream()\n                    .map(mysqlRoutineConverter::procedureParameter2routineParameter)\n                    .filter(Objects::nonNull)\n                    .sorted((left, right) -> Integer.compare(left.ordinalPosition(), right.ordinalPosition()))\n                    .toList();\n        } catch (Exception e) {\n            throw new BusinessException(\"routine.operation.parameterLoadFailed\", new Object[]{e.getMessage()}, e);\n        }\n    }\n\n    private String normalizeRoutineType(String routineType) {\n        String normalized = StringUtils.trimToEmpty(routineType).toUpperCase(Locale.ROOT);\n        if (!FUNCTION.equals(normalized) && !PROCEDURE.equals(normalized)) {\n            throw new BusinessException(\"routine.operation.typeUnsupported\");\n        }\n        return normalized;\n    }\n\n    private String requireRoutineName(RoutineOperation operation) {\n        String routineName = StringUtils.trimToEmpty(operation.getRoutineName());\n        if (StringUtils.isBlank(routineName)) {\n            throw new BusinessException(\"routine.operation.nameRequired\");\n        }\n        return routineName;\n    }\n\n    private String requireDatabaseName(RoutineOperation operation) {\n        String databaseName = StringUtils.trimToEmpty(operation.getDatabaseName());\n        if (StringUtils.isBlank(databaseName)) {\n            throw new BusinessException(\"routine.operation.databaseRequired\");\n        }\n        return databaseName;","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-plugins/chat2db-community-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlRoutineManager.java#L310-L346","documentation":"Thrown by normalizeRoutineType when operation.getRoutineType(), after trim+uppercase, is neither FUNCTION nor PROCEDURE. This is a hard validation: only those two routine kinds are supported by the MySQL routine manager. Any other value (including null, empty, 'TRIGGER', 'EVENT') is rejected before any SQL is built.","triggerScenarios":"Calling any routine-manager entry point (previewInvocation, previewMigration, executeMigration) with operation.getRoutineType() returning a value other than FUNCTION/PROCEDURE, case-insensitively.","commonSituations":"Frontend sends the routine type from a dropdown that includes unsupported kinds; a null/blank routineType field in the request payload; a copy of the operation object left with a default/null routineType; mislabeling an EVENT or TRIGGER as a routine.","solutions":["Set operation.setRoutineType(\"FUNCTION\") or \"PROCEDURE\" (case-insensitive) before calling.","Validate the routineType at the request boundary and reject unsupported kinds with a clear message.","If the value comes from metadata, map it to FUNCTION/PROCEDURE and skip other kinds.","Confirm the RoutineOperation builder is not leaving routineType unset."],"exampleFix":"// before\noperation.setRoutineType(rawType);\nroutineManager.previewInvocation(connection, operation);\n\n// after\nString type = StringUtils.trimToEmpty(rawType).toUpperCase(Locale.ROOT);\nif (!\"FUNCTION\".equals(type) && !\"PROCEDURE\".equals(type)) {\n    throw new IllegalArgumentException(\"Unsupported routine type: \" + rawType);\n}\noperation.setRoutineType(type);\nroutineManager.previewInvocation(connection, operation);","handlingStrategy":"validation","validationCode":"String t = StringUtils.trimToEmpty(rawType).toUpperCase(Locale.ROOT);\nif (!\"FUNCTION\".equals(t) && !\"PROCEDURE\".equals(t)) {\n    throw new IllegalArgumentException(\"Unsupported routine type: \" + rawType);\n}\noperation.setRoutineType(t);","typeGuard":"static boolean isSupportedRoutineType(String type) {\n    String t = StringUtils.trimToEmpty(type).toUpperCase(Locale.ROOT);\n    return \"FUNCTION\".equals(t) || \"PROCEDURE\".equals(t);\n}","tryCatchPattern":null,"preventionTips":["Restrict the routine-type picker to FUNCTION/PROCEDURE.","Map metadata routine types to FUNCTION/PROCEDURE at the boundary.","Reject unsupported kinds early with a clear validation message.","Never leave routineType unset on a RoutineOperation."],"tags":["mysql","routine","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}