{"record":{"id":"4b381188cc2172e5","repo":"router-for-me/CLIProxyAPI","slug":"missing-required-field-source-url","errorCode":null,"errorMessage":"missing required field source-url","messagePattern":"missing required field source-url","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/manifest.go","lineNumber":180,"sourceCode":"\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\")\n\t}\n\tif hasSensitiveQueryParameter(parsed) {\n\t\treturn fmt.Errorf(\"source-url contains sensitive query parameter\")\n\t}\n\treturn nil\n}\n","sourceCodeStart":162,"sourceCodeEnd":194,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/manifest.go#L162-L194","documentation":"validateManifestSourceURL() rejects a direct-install manifest whose source-url is empty after trimming. Direct installs require a resolvable source link for provenance even though artifacts carry their own URLs.","triggerScenarios":"Direct-install Manifest with SourceURL unset, \"\", or whitespace; reaching here means the install plan had no artifacts branch (len(plan.Artifacts) == 0), so the source-url check is the final gate.","commonSituations":"Minimal test manifests omitting provenance; field name drift (source: vs source-url:); deleting the field while refactoring from github-release to direct installs.","solutions":["Add source-url: https://github.com/acme/plug to the manifest","Check the exact key name is source-url and it unmarshals into Manifest.SourceURL","Remember it must be a full http(s) URL with host, or errors 691/692 follow"],"exampleFix":"# before\nversion: 1.0.0\ninstall: {type: direct}\n# no source-url -> missing required field source-url\n\n# after\nversion: 1.0.0\ninstall: {type: direct}\nsource-url: https://github.com/acme/plug","handlingStrategy":"validation","validationCode":"if m.InstallType() == pluginstore.InstallTypeDirect && strings.TrimSpace(m.SourceURL) == \"\" && len(pluginstore.NormalizeInstallPlan(m.Install).Artifacts) == 0 {\n    return errors.New(\"source-url required for artifact-less direct install\")\n}\n_ = m.Validate()","typeGuard":"func hasSourceURL(m pluginstore.Manifest) bool { return strings.TrimSpace(m.SourceURL) != \"\" }","tryCatchPattern":"if err := m.Validate(); err != nil && strings.Contains(err.Error(), \"missing required field source-url\") { /* set upstream https URL, re-validate */ }","preventionTips":["Always populate source-url with the upstream repo HTTPS URL","Manifest template with all required keys pre-filled"],"tags":["pluginstore","manifest","source-url","validation"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}