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

  1. URL-encode the artifact URL properly (percent-encode spaces and special characters)
  2. Verify each artifact URL with url.Parse (or curl) before shipping the manifest
  3. 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

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


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