router-for-me/CLIProxyAPI · error

invalid plugin id %q

Error message

invalid plugin id %q

What it means

validateManifestPluginID() requires the id (after trim) to match ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$: start with an ASCII letter or digit, continue with letters/digits/dot/underscore/hyphen only, max 128 chars. Spaces, slashes, colons, unicode, or a leading punctuation char all fail.

Source

Thrown at internal/pluginstore/manifest.go:172

			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)
	if sourceURL == "" {
		return fmt.Errorf("missing required field source-url")
	}
	parsed, errParse := url.Parse(sourceURL)
	if errParse != nil || parsed.Scheme == "" || parsed.Host == "" {
		return fmt.Errorf("invalid source-url")
	}
	if parsed.Scheme != "https" && parsed.Scheme != "http" {
		return fmt.Errorf("source-url must use http or https")
	}
	if hasSensitiveQueryParameter(parsed) {
		return fmt.Errorf("source-url contains sensitive query parameter")

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Rewrite the id to the allowed alphabet: alphanumerics, dots, underscores, hyphens; e.g. "acme.repo-plugin"
  2. Ensure the first character is a letter or digit (not '.', '_', '-')
  3. Keep it under 128 characters and avoid spaces/slashes entirely

Example fix

# before
id: "acme/plugin one"
# invalid plugin id "acme/plugin one"

# after
id: "acme.plugin-one"
Defensive patterns

Strategy: validation

Validate before calling

var idRe = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$`)
if !idRe.MatchString(strings.TrimSpace(m.ID)) { return fmt.Errorf("bad id %q", m.ID) }
// or reuse the public helper from pluginhost:
if !pluginhost.ValidatePluginID(strings.TrimSpace(m.ID)) { return errors.New("bad id") }

Type guard

func validPluginIDString(id string) bool { return regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$`).MatchString(strings.TrimSpace(id)) }

Try / catch

if err := m.Validate(); err != nil && strings.Contains(err.Error(), "invalid plugin id") { /* slugify: replace / and space with '-', check length<=128, re-validate */ }

Prevention

When it happens

Trigger: Direct-install manifest with id like "acme/plugin" (slash), "my plugin" (space), "1.0:plug" (colon), a 129+ char id, or one starting with "_" or ".".

Common situations: Using a repository path (owner/repo) as the plugin id; ids copied from display names with spaces; i18n ids with non-ASCII characters; very long generated ids exceeding 128 chars.

Related errors


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