{"record":{"id":"52b3646f9d88ff56","repo":"apache/incubator-seata","slug":"tcc-bean-name-cannot-be-null-or-empty","errorCode":null,"errorMessage":"TCC bean name cannot be null or empty","messagePattern":"TCC bean name cannot be null or empty","errorType":"exception","errorClass":"FrameworkException","httpStatus":null,"severity":"error","filePath":"compatible/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java","lineNumber":68,"sourceCode":"\n        // register resource and enhance with interceptor\n        registerResource(target, methodClassMap);\n\n        return new TccActionInterceptorHandler(\n                target, methodsToProxy.stream().map(Method::getName).collect(Collectors.toSet()));\n    }\n\n    @Override\n    protected Class<? extends Annotation> getAnnotationClass() {\n        return TwoPhaseBusinessAction.class;\n    }\n\n    protected Resource createResource(Object target, Class<?> targetServiceClass, Method m, Annotation annotation)\n            throws NoSuchMethodException {\n        TwoPhaseBusinessAction twoPhaseBusinessAction = (TwoPhaseBusinessAction) annotation;\n        TCCResource tccResource = new TCCResource();\n        if (StringUtils.isBlank(twoPhaseBusinessAction.name())) {\n            throw new FrameworkException(\"TCC bean name cannot be null or empty\");\n        }\n        tccResource.setActionName(twoPhaseBusinessAction.name());\n        tccResource.setTargetBean(target);\n        tccResource.setPrepareMethod(m);\n        tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod());\n        tccResource.setCommitMethod(targetServiceClass.getMethod(\n                twoPhaseBusinessAction.commitMethod(), twoPhaseBusinessAction.commitArgsClasses()));\n        tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod());\n        tccResource.setRollbackMethod(targetServiceClass.getMethod(\n                twoPhaseBusinessAction.rollbackMethod(), twoPhaseBusinessAction.rollbackArgsClasses()));\n        // set argsClasses\n        tccResource.setCommitArgsClasses(twoPhaseBusinessAction.commitArgsClasses());\n        tccResource.setRollbackArgsClasses(twoPhaseBusinessAction.rollbackArgsClasses());\n        // set phase two method's keys\n        tccResource.setPhaseTwoCommitKeys(\n                this.getTwoPhaseArgs(tccResource.getCommitMethod(), twoPhaseBusinessAction.commitArgsClasses()));\n        tccResource.setPhaseTwoRollbackKeys(\n                this.getTwoPhaseArgs(tccResource.getRollbackMethod(), twoPhaseBusinessAction.rollbackArgsClasses()));","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/apache/incubator-seata/blob/e01f97c6db397165050caa6764020410c2c8199a/compatible/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java#L50-L86","documentation":"Thrown by the compatible TCC interceptor parser when a @TwoPhaseBusinessAction annotation has a blank name attribute. The name becomes the TCCResource actionName (the resource identity registered with the TC), so an empty name would make the branch unresolvable and is rejected at parser time.","triggerScenarios":"Declaring @TwoPhaseBusinessAction(commitMethod=..., rollbackMethod=...) without setting name, or setting name = \"\" / whitespace. Detected when the parser scans the bean and builds the TCCResource.","commonSituations":"Copy-pasted TCC annotations with the name attribute accidentally deleted; refactoring that moved the annotation but dropped its attributes; assuming name defaults to the method name (it does not).","solutions":["Set a unique non-empty name on every @TwoPhaseBusinessAction, e.g. name = \"createOrderTccAction\".","Keep names stable across releases — they identify the branch resource on the TC.","Add a build-time or test-time check that all TwoPhaseBusinessAction annotations have names."],"exampleFix":"// before\n@TwoPhaseBusinessAction(commitMethod = \"commit\", rollbackMethod = \"rollback\")\npublic boolean prepare(BusinessActionContext ctx, Long id) { ... }\n\n// after\n@TwoPhaseBusinessAction(name = \"createOrderTccAction\", commitMethod = \"commit\", rollbackMethod = \"rollback\")\npublic boolean prepare(BusinessActionContext ctx, Long id) { ... }","handlingStrategy":"validation","validationCode":"TwoPhaseBusinessAction a = method.getAnnotation(TwoPhaseBusinessAction.class);\nif (a == null || a.name() == null || a.name().trim().isEmpty()) {\n    throw new IllegalStateException(\"@TwoPhaseBusinessAction on \" + method + \" needs a non-empty name\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    // parser scans bean at startup\n} catch (FrameworkException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"TCC bean name\")) {\n        throw new ConfigurationException(\"a @TwoPhaseBusinessAction is missing its name attribute\", e);\n    }\n    throw e;\n}","preventionTips":["Set name on every @TwoPhaseBusinessAction; treat it as mandatory, not optional.","Add an ArchUnit/reflection test asserting all TwoPhaseBusinessAction annotations have non-blank names.","Keep names stable — they key the branch resource on the TC."],"tags":["tcc","annotation","configuration","validation"],"backgroundTag":null,"analyzedSha":"e01f97c6db397165050caa6764020410c2c8199a","analyzedAt":"2026-08-14T10:23:53.097Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}