router-for-me/CLIProxyAPI · error
artifacts[%d]: pinned artifact url must not contain query or
Error message
artifacts[%d]: pinned artifact url must not contain query or fragment
What it means
validatePinnedArtifactURLs() requires artifact URLs to be bare: any query string (?...) or fragment (#...) makes the pin ambiguous or a channel for hidden tokens, so artifacts[index] is rejected when parsed.RawQuery or parsed.Fragment is non-empty.
Source
Thrown at internal/pluginstore/manifest.go:160
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)
}
return nil
}
func validateManifestSourceURL(sourceURL string) error {
sourceURL = strings.TrimSpace(sourceURL)View on GitHub (pinned to 78f0c4079e)
Solutions
- Strip the query string and fragment from the artifact URL; if a checksum is needed, put it in the manifest's dedicated checksum field if the plan schema provides one
- Use plain, stable, public artifact paths (path tokens are fine, query strings are not)
- If you relied on signed URLs for access control, switch to public artifacts or path-based short tokens
Example fix
# before artifacts: - url: https://dl.acme.io/plug.so?token=abc123 # artifacts[0]: pinned artifact url must not contain query or fragment # after artifacts: - url: https://dl.acme.io/plug.so
Defensive patterns
Strategy: validation
Validate before calling
for i, a := range plan.Artifacts {
if u, err := url.Parse(strings.TrimSpace(a.URL)); err == nil && (u.RawQuery != "" || u.Fragment != "") {
return fmt.Errorf("artifacts[%d] carries query/fragment", i)
}
}
_ = m.Validate() Type guard
func barePinnedURL(raw string) bool { u, err := url.Parse(strings.TrimSpace(raw)); return err == nil && u.User == nil && u.RawQuery == "" && u.Fragment == "" } Try / catch
if err := m.Validate(); err != nil && strings.Contains(err.Error(), "query or fragment") { /* strip ?... and #..., re-validate */ } Prevention
- Use plain artifact paths, not signed/tracking URLs
- Put checksums in the manifest's checksum fields, not URL fragments
When it happens
Trigger: Artifact URL with tracking params ("...plug.so?src=registry"), a pre-signed AWS/GCS URL ("...?X-Goog-Signature=..."), or an anchor ("...plug.so#sha256-abc").
Common situations: Copying CDN or object-store links that append signature/token query params; attaching checksum intents as fragments; analytics parameters added by whatever tool generated the link.
Related errors
- artifacts[%d]: pinned artifact url must not contain credenti
- direct plugin sync artifact %d must use https
- zip entry has empty name
- zip entry %s uses backslash path separators
- zip entry %s is absolute
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/bd8171a5d7a9013b.
Report an issue: GitHub.