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
- Rewrite the id to the allowed alphabet: alphanumerics, dots, underscores, hyphens; e.g. "acme.repo-plugin"
- Ensure the first character is a letter or digit (not '.', '_', '-')
- 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
- Slugify ids when generating from repo names (owner/repo → owner.repo)
- Cap ids at 128 chars at generation time
- Reuse pluginhost.ValidatePluginID for one source of truth
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
- missing required field id
- github-release manifest requires a resolved release
- unsupported install type %q
- missing required field version
- invalid plugin version %q
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/685a2eecb5bf71cb.
Report an issue: GitHub.