{"record":{"id":"b1bdac1b2c7c3095","repo":"Tencent/WeKnora","slug":"favorite-resource-id-is-required","errorCode":null,"errorMessage":"favorite resource id is required","messagePattern":"favorite resource id is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/application/service/user_resource_favorite.go","lineNumber":16,"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) {\n\tif !types.IsValidFavoriteResourceType(resourceType) {","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/user_resource_favorite.go#L1-L34","documentation":"ErrFavoriteEmptyID is a sentinel error returned by the favorite service's Add and Remove operations when the resourceID argument is empty or whitespace-only. A favorite must reference a concrete resource, so the service rejects blank IDs before hitting the repository, keeping GORM errors out of the HTTP layer.","triggerScenarios":"Calling Add/AddFavorite or Remove/RemoveFavorite with resourceID == \"\" or only whitespace (e.g. an unset field in the request struct, a URL param that failed to bind).","commonSituations":"Client omits the resource id in the request body/path; router param name mismatch leaves the variable empty; upstream code passes a zero-value struct field.","solutions":["Ensure the caller supplies the resource ID; check request binding/route params.","Validate the ID is non-empty at the handler layer and return 400 before invoking the service.","If IDs are generated upstream, confirm the generating step actually ran (e.g. resource was created successfully)."],"exampleFix":"// before\nid := req.ResourceID // may be \"\"\nsvc.AddFavorite(ctx, userID, tenantID, resourceType, id)\n// after\nif strings.TrimSpace(req.ResourceID) == \"\" {\n    return httpError(400, \"resource id is required\")\n}\nsvc.AddFavorite(ctx, userID, tenantID, resourceType, req.ResourceID)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(resourceID) == \"\" {\n    return httpError(400, \"resource id is required\")\n}","typeGuard":null,"tryCatchPattern":"// handler\nif errors.Is(err, service.ErrFavoriteEmptyID) {\n    return httpError(400, err.Error())\n}","preventionTips":["Make resourceID a required request binding field with binding:\"required\".","Check route param names match the handler extraction code.","Never pass zero-value struct fields straight into favorite calls."],"tags":["validation","favorites","missing-argument","sentinel-error"],"backgroundTag":"missing-required-argument","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}