{"record":{"id":"ce3a7621f3552bc0","repo":"router-for-me/CLIProxyAPI","slug":"missing-required-field-id","errorCode":null,"errorMessage":"missing required field id","messagePattern":"missing required field id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/manifest.go","lineNumber":169,"sourceCode":"\tfor index, artifact := range artifacts {\n\t\tparsed, errParse := url.Parse(strings.TrimSpace(artifact.URL))\n\t\tif errParse != nil {\n\t\t\treturn fmt.Errorf(\"artifacts[%d]: invalid artifact url\", index)\n\t\t}\n\t\tif parsed.User != nil {\n\t\t\treturn fmt.Errorf(\"artifacts[%d]: pinned artifact url must not contain credentials\", index)\n\t\t}\n\t\tif parsed.RawQuery != \"\" || parsed.Fragment != \"\" {\n\t\t\treturn fmt.Errorf(\"artifacts[%d]: pinned artifact url must not contain query or fragment\", index)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validateManifestPluginID(id string) error {\n\tid = strings.TrimSpace(id)\n\tif id == \"\" {\n\t\treturn fmt.Errorf(\"missing required field id\")\n\t}\n\tif !validPluginID(id) {\n\t\treturn fmt.Errorf(\"invalid plugin id %q\", id)\n\t}\n\treturn nil\n}\n\nfunc validateManifestSourceURL(sourceURL string) error {\n\tsourceURL = strings.TrimSpace(sourceURL)\n\tif sourceURL == \"\" {\n\t\treturn fmt.Errorf(\"missing required field source-url\")\n\t}\n\tparsed, errParse := url.Parse(sourceURL)\n\tif errParse != nil || parsed.Scheme == \"\" || parsed.Host == \"\" {\n\t\treturn fmt.Errorf(\"invalid source-url\")\n\t}\n\tif parsed.Scheme != \"https\" && parsed.Scheme != \"http\" {\n\t\treturn fmt.Errorf(\"source-url must use http or https\")","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/manifest.go#L151-L187","documentation":"validateManifestPluginID() rejects a direct-install manifest whose id is empty after trimming. The id is the plugin's configuration key and install identity, so it is mandatory.","triggerScenarios":"Direct-install Manifest with ID set to \"\", \"   \", or a value that trims to empty; commonly a YAML key mismatch (name: instead of id:) so the field never populates.","commonSituations":"Renaming manifest fields during a format migration; JSON unmarshaling into a different struct tag; generating manifests programmatically and forgetting to set the id.","solutions":["Set id to a stable identifier, e.g. \"acme.plugin\"","Confirm the manifest key is exactly id and maps to Manifest.ID","Re-run Validate() — this error surfaces before the format checks in error 689, so fix emptiness first"],"exampleFix":"# before\nid: \"\"   # or key misspelled as name:\n# missing required field id\n\n# after\nid: acme.plugin","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(m.ID) == \"\" && m.InstallType() == pluginstore.InstallTypeDirect {\n    return errors.New(\"manifest id required\")\n}\n_ = m.Validate()","typeGuard":"func hasPluginID(m pluginstore.Manifest) bool { return strings.TrimSpace(m.ID) != \"\" }","tryCatchPattern":"if err := m.Validate(); err != nil && strings.Contains(err.Error(), \"missing required field id\") { /* set ID then re-validate */ }","preventionTips":["Required-field checklist per install type in your generator","Unmarshal manifest into a typed struct so missing keys are obvious in tests"],"tags":["pluginstore","manifest","id","validation"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}