{"record":{"id":"d8ecb989ad05a56c","repo":"Tencent/WeKnora","slug":"invalid-favorite-resource-type","errorCode":null,"errorMessage":"invalid favorite resource type","messagePattern":"invalid favorite resource type","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/application/service/user_resource_favorite.go","lineNumber":15,"sourceCode":"package service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strings\"\n\n\t\"github.com/Tencent/WeKnora/internal/types\"\n\t\"github.com/Tencent/WeKnora/internal/types/interfaces\"\n)\n\n// Sentinel errors so the handler can map cleanly to HTTP status codes\n// without leaking GORM internals.\nvar (\n\tErrFavoriteInvalidType = errors.New(\"invalid favorite resource type\")\n\tErrFavoriteEmptyID     = errors.New(\"favorite resource id is required\")\n)\n\ntype userResourceFavoriteService struct {\n\trepo interfaces.UserResourceFavoriteRepository\n}\n\n// NewUserResourceFavoriteService wraps the repository with input\n// validation (allowlist of resource types, non-empty resource id).\n// We keep service thin on purpose — favoriting is a non-business action\n// that doesn't need audit logging or cross-aggregate side effects.\nfunc NewUserResourceFavoriteService(repo interfaces.UserResourceFavoriteRepository) interfaces.UserResourceFavoriteService {\n\treturn &userResourceFavoriteService{repo: repo}\n}\n\nfunc (s *userResourceFavoriteService) List(\n\tctx context.Context, userID string, tenantID uint64, resourceType string,\n) ([]*types.UserResourceFavorite, error) {","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/user_resource_favorite.go#L1-L33","documentation":"ErrFavoriteInvalidType is a sentinel error returned by the user resource favorite service when a resourceType argument is not one of the recognized favorite resource types. The service validates via types.IsValidFavoriteResourceType before touching the repository, so callers get a clean domain error instead of GORM internals. It applies to List, Add, Remove and their handler-facing wrappers.","triggerScenarios":"Calling ListFavorites/AddFavorite/RemoveFavorite (or the service methods List/Add/Remove) with a resourceType string not accepted by types.IsValidFavoriteResourceType — e.g. a typo, different casing, or a new type the validator does not know.","commonSituations":"Client sends an arbitrary resource type in a request path/body; frontend and backend type enums drift after adding a new favoritable resource; case-sensitive mismatch like \"Document\" vs \"document\".","solutions":["Check the accepted values in types.IsValidFavoriteResourceType and send one of those exact strings.","Normalize the resource type (trim/lowercase) at the handler boundary before calling the service.","If a new resource type should be favoritable, add it to the validator's allowed set."],"exampleFix":"// before\nsvc.AddFavorite(ctx, userID, tenantID, \"Documents\", resourceID)\n// after (use an allowed type constant)\nsvc.AddFavorite(ctx, userID, tenantID, types.FavoriteResourceTypeDocument, resourceID)","handlingStrategy":"validation","validationCode":"if !types.IsValidFavoriteResourceType(resourceType) {\n    return httpError(400, fmt.Sprintf(\"unsupported resource type %q\", resourceType))\n}","typeGuard":null,"tryCatchPattern":"// handler\nif errors.Is(err, service.ErrFavoriteInvalidType) {\n    return httpError(400, err.Error())\n}","preventionTips":["Reuse the types constants/enums for resource types instead of raw strings.","Normalize (trim/lowercase) resource type input at the API boundary.","Add a test asserting every new favoritable type passes IsValidFavoriteResourceType."],"tags":["validation","favorites","sentinel-error"],"backgroundTag":"invalid-enum-value","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}