router-for-me/CLIProxyAPI · error
artifacts[%d]: invalid artifact url
Error message
artifacts[%d]: invalid artifact url
What it means
validatePinnedArtifactURLs() url.Parse()s each trimmed artifact URL in a direct install plan; a parse failure (control characters, unmatched %, malformed bracket host, etc.) rejects artifacts[index] with this message. Only syntactic parseability is checked here.
Source
Thrown at internal/pluginstore/manifest.go:154
}
releaseVersion, errVersion := ReleaseVersion(Release{TagName: releaseTag})
if errVersion != nil {
return errVersion
}
if releaseVersion != normalizeVersion(version) {
return fmt.Errorf("release-tag %q resolves version %q, want %q", releaseTag, releaseVersion, normalizeVersion(version))
}
return nil
default:
return fmt.Errorf("unsupported install type %q", m.Install.Type)
}
}
func validatePinnedArtifactURLs(artifacts []Artifact) error {
for index, artifact := range artifacts {
parsed, errParse := url.Parse(strings.TrimSpace(artifact.URL))
if errParse != nil {
return fmt.Errorf("artifacts[%d]: invalid artifact url", index)
}
if parsed.User != nil {
return fmt.Errorf("artifacts[%d]: pinned artifact url must not contain credentials", index)
}
if parsed.RawQuery != "" || parsed.Fragment != "" {
return fmt.Errorf("artifacts[%d]: pinned artifact url must not contain query or fragment", index)
}
}
return nil
}
func validateManifestPluginID(id string) error {
id = strings.TrimSpace(id)
if id == "" {
return fmt.Errorf("missing required field id")
}
if !validPluginID(id) {
return fmt.Errorf("invalid plugin id %q", id)View on GitHub (pinned to 78f0c4079e)
Solutions
- URL-encode the artifact URL properly (percent-encode spaces and special characters)
- Verify each artifact URL with url.Parse (or curl) before shipping the manifest
- Build URLs with net/url.URL or a templating filter that escapes, not naive concatenation
Example fix
# before artifacts: - url: http://dl.acme.com/my plugin.so # artifacts[0]: invalid artifact url # after artifacts: - url: http://dl.acme.com/my%20plugin.so
Defensive patterns
Strategy: validation
Validate before calling
for i, a := range plan.Artifacts {
if _, err := url.Parse(strings.TrimSpace(a.URL)); err != nil {
return fmt.Errorf("artifacts[%d]: bad url: %w", i, err)
}
}
_ = m.Validate() Type guard
func parseableURL(u string) bool { _, err := url.Parse(strings.TrimSpace(u)); return err == nil } Try / catch
if err := m.Validate(); err != nil && strings.Contains(err.Error(), "invalid artifact url") { /* percent-encode offending URL, re-validate */ } Prevention
- Build artifact URLs with net/url or an escaping template filter
- Verify every artifact URL with url.Parse in CI manifest lint
When it happens
Trigger: Direct-install manifest whose install.artifacts[i].url contains characters net/url cannot parse: raw spaces, "%%", "http://ex ample.com/x.so", or a URL built by string concatenation with unescaped input.
Common situations: Templating that injects filenames with spaces into URLs; Windows paths pasted as URLs ("C:\libs\plug.dll"); encoding bugs producing double-escapes.
Related errors
- invalid source-url
- direct plugin sync artifact %d must use https
- github-release manifest requires a resolved release
- unsupported install type %q
- missing required field version
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/d5eb6e415d52e31b.
Report an issue: GitHub.