{"record":{"id":"bee092cd399ff933","repo":"iflytek/astron-agent","slug":"response-failed-creating-custom-categories-requires","errorCode":"RESPONSE_FAILED","errorMessage":"Creating custom categories requires recording creator UID","messagePattern":"Creating custom categories requires recording creator UID","errorType":"error_code","errorClass":"BusinessException","httpStatus":null,"severity":"error","filePath":"console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelCategoryService.java","lineNumber":129,"sourceCode":"        return roots;\n    }\n\n    /**\n     * Save four types of model configurations (all implemented through category relationships)\n     *\n     * @param req Input parameter containing systemIds / customNames for each dimension\n     */\n    @Transactional(rollbackFor = Exception.class)\n    public void saveAll(ModelCategoryReq req) {\n        if (req == null || req.getModelId() == null) {\n            return;\n        }\n        final Long modelId = req.getModelId();\n        boolean hasAnyCustom =\n                (req.getCategoryCustom() != null && StringUtils.isNotBlank(req.getCategoryCustom().getCustomName())) ||\n                        (req.getSceneCustom() != null && StringUtils.isNotBlank(req.getSceneCustom().getCustomName()));\n        if (hasAnyCustom && req.getOwnerUid() == null) {\n            throw new BusinessException(ResponseEnum.RESPONSE_FAILED, \"Creating custom categories requires recording creator UID\");\n        }\n\n        // ---------- 1) Preprocess official IDs (deduplicate, remove empty) ----------\n        List<Long> catSys = safeDistinctIds(req.getCategorySystemIds());\n        List<Long> sceneSys = safeDistinctIds(req.getSceneSystemIds());\n\n        // ---------- 2) Preprocess custom items (trim names; treat blank names as not provided) ----------\n        ModelCategoryReq.CustomItem catCustom = normalizeCustom(req.getCategoryCustom());\n        ModelCategoryReq.CustomItem sceneCustom = normalizeCustom(req.getSceneCustom());\n\n        // ---------- 3) Custom item parent-child dimension & status validation ----------\n        // Rule: pid must exist in model_category, and p.is_delete=0, and p.key matches target dimension key\n        if (catCustom != null) {\n            assertParentOk(catCustom.getPid(), \"modelCategory\");\n        }\n        if (sceneCustom != null) {\n            assertParentOk(sceneCustom.getPid(), \"modelScenario\");\n        }","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelCategoryService.java#L111-L147","documentation":"ModelCategoryService.saveAll throws this when the request tries to save at least one user-defined (custom) category or scene name but does not carry ownerUid. Custom categories are per-user data, so the platform must record which user created them; without a creator UID the insert would be unattributable, so the service rejects the request up front.","triggerScenarios":"Calling saveAll with categoryCustom.customName or sceneCustom.customName set (non-blank) while req.ownerUid is null. Typically happens when a client builds the ModelCategorySaveRequest from an unauthenticated context or forgets to propagate the current user's UID into the request object.","commonSituations":"Admin/internal tooling calling the category-save API without logging in as a user; a frontend passing the body through without the uid field; service-to-service calls that strip ownerUid; tests constructing the request with only the custom name.","solutions":["Populate req.setOwnerUid(...) with the current authenticated user's UID before calling saveAll","Ensure the controller/interceptor injects the logged-in user's UID into the request DTO (e.g. from the auth context)","If no custom name is intended, clear categoryCustom/sceneCustom (or leave customName blank) so the check does not fire","Add a request-level validation (e.g. @NotNull on ownerUid when customName present) so callers fail fast with a clearer message"],"exampleFix":"// before\nModelCategorySaveRequest req = new ModelCategorySaveRequest();\nreq.setCategoryCustom(CustomItem.of(\"My Custom Tag\"));\nmodelCategoryService.saveAll(req);\n// after\nreq.setOwnerUid(currentUserId); // from auth context\nmodelCategoryService.saveAll(req);","handlingStrategy":"validation","validationCode":"boolean hasCustom = (req.getCategoryCustom() != null && req.getCategoryCustom().getCustomName() != null && !req.getCategoryCustom().getCustomName().isBlank()) || (req.getSceneCustom() != null && req.getSceneCustom().getCustomName() != null && !req.getSceneCustom().getCustomName().isBlank());\nif (hasCustom && req.getOwnerUid() == null) throw new IllegalArgumentException(\"ownerUid is required when submitting custom categories\");","typeGuard":"boolean isCustomSubmit(ModelCategorySaveRequest req) { return req != null && ((req.getCategoryCustom() != null && hasText(req.getCategoryCustom().getCustomName())) || (req.getSceneCustom() != null && hasText(req.getSceneCustom().getCustomName()))); }","tryCatchPattern":"try { modelCategoryService.saveAll(req); } catch (BusinessException e) { if (e.getMessage() != null && e.getMessage().contains(\"creator UID\")) { return 400-uid-required; } throw e; }","preventionTips":["Always derive ownerUid from the authenticated principal in the controller layer, never from client body","Add bean validation (@AssertTrue) on the DTO enforcing ownerUid presence when custom names are set","Write a unit test covering custom-name-without-uid","Never strip fields when mapping DTOs between layers"],"tags":["validation","missing-argument","java","spring"],"backgroundTag":"missing-required-argument","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}