{"record":{"id":"99f34a5149bcc0c0","repo":"router-for-me/CLIProxyAPI","slug":"invalid-source-url","errorCode":null,"errorMessage":"invalid source-url","messagePattern":"invalid source-url","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/manifest.go","lineNumber":184,"sourceCode":"func 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":166,"sourceCodeEnd":194,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/manifest.go#L166-L194","documentation":"validateManifestSourceURL() url.Parse()s the trimmed source-url and requires both a scheme and a host. Parse failure, scheme-less (\"github.com/acme/plug\"), or host-less (\"https:\" alone) values are rejected as unparseable/unusable.","triggerScenarios":"source-url: \"github.com/acme/plug\" (no scheme); \"https:///path\" (no host); any string net/url cannot parse (control chars, bad percent-encoding).","commonSituations":"Copying a bare repo slug instead of a URL; trailing configuration where the scheme got stripped; YAML quoting issues mangling the value (e.g. colon-space inside an unquoted scalar).","solutions":["Write the full absolute URL including scheme and host: https://github.com/acme/plug","Quote the value in YAML to avoid parser mangling","Test the URL with url.Parse or by opening it in a browser before shipping"],"exampleFix":"# before\nsource-url: github.com/acme/plug\n# invalid source-url\n\n# after\nsource-url: https://github.com/acme/plug","handlingStrategy":"validation","validationCode":"u, err := url.Parse(strings.TrimSpace(m.SourceURL))\nif err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return errors.New(\"source-url must be an absolute URL with host\")\n}\n_ = m.Validate()","typeGuard":"func absoluteSourceURL(raw string) bool { u, err := url.Parse(strings.TrimSpace(raw)); return err == nil && u.Scheme != \"\" && u.Host != \"\" }","tryCatchPattern":"if err := m.Validate(); err != nil && err.Error() == \"invalid source-url\" { /* prepend https:// if slug-like, re-validate */ }","preventionTips":["Always paste the full https:// URL","Quote URLs in YAML to avoid scalar-parsing damage"],"tags":["pluginstore","manifest","source-url","url-parsing"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}