{"record":{"id":"9dc8a73d48f9ff05","repo":"GopeedLab/gopeed","slug":"extension-name-is-required","errorCode":null,"errorMessage":"extension name is required","messagePattern":"extension name is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/download/extension.go","lineNumber":438,"sourceCode":"\tHomepage string `json:\"homepage\"`\n\t// Repository git repository info\n\tRepository *Repository `json:\"repository\"`\n\tScripts    []*Script   `json:\"scripts\"`\n\tSettings   []*Setting  `json:\"settings\"`\n\t// Disabled if true, this extension will be ignored\n\tDisabled bool `json:\"disabled\"`\n\n\tDevMode bool `json:\"devMode\"`\n\t// DevPath is the local path of extension source code\n\tDevPath string `json:\"devPath\"`\n\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n}\n\nfunc (e *Extension) validate() error {\n\tif e.Name == \"\" {\n\t\treturn fmt.Errorf(\"extension name is required\")\n\t}\n\tif e.Title == \"\" {\n\t\treturn fmt.Errorf(\"extension title is required\")\n\t}\n\tif e.Version == \"\" {\n\t\treturn fmt.Errorf(\"extension version is required\")\n\t}\n\treturn nil\n}\n\nfunc (e *Extension) buildIdentity() string {\n\tif e.Author == \"\" {\n\t\treturn e.Name\n\t}\n\treturn e.Author + \"@\" + e.Name\n}\n\nfunc (e *Extension) buildInstallUrl() string {","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/download/extension.go#L420-L456","documentation":"Extension.validate() requires a non-empty name in manifest.json; parseExtensionByPath runs it right after unmarshalling. The name is the machine identifier (combined with author in buildIdentity as author@name), so an empty name would break storage keys, the extensions directory path, and lookup by identity.","triggerScenarios":"manifest.json without a \"name\" key, or \"name\": \"\" / whitespace-only; a manifest where name is nested under another object or misspelled (\"Name\", \"id\") so json.Unmarshal leaves the field zero.","commonSituations":"Hand-written manifests from a template with placeholders never filled; renaming keys when porting a browser-extension manifest.json (which uses different casing); JSON comments or trailing commas causing partial unmarshal issues.","solutions":["Add a non-empty, unique \"name\" (lowercase slug recommended) to manifest.json","Confirm the key is exactly \"name\" and at the top level","Validate the manifest with a JSON linter to catch structural mistakes"],"exampleFix":"// before (manifest.json)\n{ \"title\": \"My Ext\", \"version\": \"1.0.0\" }\n// after\n{ \"name\": \"my-extension\", \"title\": \"My Ext\", \"version\": \"1.0.0\" }","handlingStrategy":"validation","validationCode":"// Go: pre-validate manifest before install\nvar m struct {\n    Name    string `json:\"name\"`\n    Title   string `json:\"title\"`\n    Version string `json:\"version\"`\n}\nif err := json.Unmarshal(raw, &m); err != nil { return err }\nif strings.TrimSpace(m.Name) == \"\" { return fmt.Errorf(\"manifest.json: 'name' is required\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the official extension template so required keys are present","Validate the manifest in CI (jq '.name' must be non-empty)","Use a lowercase slug name; it becomes part of author@name identity"],"tags":["extension","manifest","validation","install"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}