{"record":{"id":"72a3d1b8cb58dbdb","repo":"GopeedLab/gopeed","slug":"invalid-resource-file-name","errorCode":null,"errorMessage":"invalid resource file name","messagePattern":"invalid resource file name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/base/model.go","lineNumber":105,"sourceCode":"\tName string `json:\"name\"`\n\tSize int64  `json:\"size\"`\n\t// is support range download\n\tRange bool `json:\"range\"`\n\t// file list\n\tFiles []*FileInfo `json:\"files\"`\n\tHash  string      `json:\"hash\"`\n}\n\nfunc (r *Resource) Validate() error {\n\tif r.Name == \"\" {\n\t\treturn fmt.Errorf(\"invalid resource name\")\n\t}\n\tif len(r.Files) == 0 {\n\t\treturn fmt.Errorf(\"invalid resource files\")\n\t}\n\tfor _, file := range r.Files {\n\t\tif file.Name == \"\" {\n\t\t\treturn fmt.Errorf(\"invalid resource file name\")\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (r *Resource) CalcSize(selectFiles []int) {\n\tvar size int64\n\tfor i, file := range r.Files {\n\t\tif len(selectFiles) == 0 || slices.Contains(selectFiles, i) {\n\t\t\tsize += file.Size\n\t\t}\n\t}\n\tr.Size = size\n}\n\ntype FileInfo struct {\n\tName  string     `json:\"name\"`\n\tPath  string     `json:\"path\"`","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/base/model.go#L87-L123","documentation":"Resource.Validate() rejects a download resource whose Files slice contains an entry with an empty Name. Validation runs both when a task/resource is submitted directly and when an extension's onResolve handler returns a resource (pkg/download/extension.go:264 logs it as 'resource invalid' and skips it). The check exists because every file entry needs a name to build the download path on disk.","triggerScenarios":"Calling the resolve/create API with a base.Resource where some Files[i].Name is \"\" (e.g. a scraper filled only URL and Size), or an extension onResolve returning gopeed.resource({name:'pkg', files:[{url:'https://host/f.bin'}]}) with the per-file name field missing.","commonSituations":"Hand-built Resource structs in Go clients; extension scripts mapping file lists from a page and dropping the filename key; JSON manifests where a file entry lacks the \"name\" key; empty filename after a bad string split on '/' of the URL path.","solutions":["Set a non-empty Name on every entry of Resource.Files before creating the task","In extension onResolve, copy the real filename into each file object of the returned resource","If the Resource comes from JSON, verify each file entry unmarshals with a non-empty name before submitting"],"exampleFix":"// before (extension onResolve)\nconst res = gopeed.resource({\n  name: 'pkg',\n  files: [{ url: 'https://host/f.bin' }],\n});\n// after\nconst res = gopeed.resource({\n  name: 'pkg',\n  files: [{ name: 'f.bin', url: 'https://host/f.bin' }],\n});","handlingStrategy":"validation","validationCode":"// Go: before creating the task\nfor i, f := range res.Files {\n    if strings.TrimSpace(f.Name) == \"\" {\n        return fmt.Errorf(\"file %d has empty name: fix or default it\", i)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In extension onResolve, always build file entries with both name and url","Log the rejected resource at warn level so skipped extensions are visible","Add a unit test that validates every Resource your code constructs"],"tags":["validation","download","extension","resource"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}