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

  1. 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
  2. Use plain, stable, public artifact paths (path tokens are fine, query strings are not)
  3. 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

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


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/bd8171a5d7a9013b. Report an issue: GitHub.