router-for-me/CLIProxyAPI · error
direct install manifest missing source-url
Error message
direct install manifest missing source-url
What it means
Thrown when resolving a direct-install plugin from a manifest that declares no inline artifacts (len(plugin.Install.Artifacts) == 0). In that case the client must fall back to a registry to resolve artifacts, using manifest.SourceURL first and c.RegistryURL second. If both are empty after trimming, there is nowhere to resolve the plugin from, so the operation fails. The library requires at least one resolution source to guarantee the artifact origin is known and auditable.
Source
Thrown at internal/pluginstore/install.go:193
}
result.InstallType = InstallTypeDirect
return result, nil
}
func (c Client) directPluginFromManifest(ctx context.Context, manifest Manifest) (Plugin, error) {
plugin := manifest.Plugin()
plugin.Version = normalizeVersion(manifest.Version)
plugin.Install = NormalizeInstallPlan(plugin.Install)
plugin.Install.Type = InstallTypeDirect
if len(plugin.Install.Artifacts) > 0 {
return plugin, nil
}
sourceURL := strings.TrimSpace(manifest.SourceURL)
if sourceURL == "" {
sourceURL = strings.TrimSpace(c.RegistryURL)
}
if sourceURL == "" {
return Plugin{}, fmt.Errorf("direct install manifest missing source-url")
}
sourceClient := c
sourceClient.RegistryURL = sourceURL
registry, errRegistry := sourceClient.FetchRegistry(ctx)
if errRegistry != nil {
return Plugin{}, fmt.Errorf("fetch direct install source: %w", errRegistry)
}
resolved, okPlugin := registry.PluginByID(manifest.ID)
if !okPlugin {
return Plugin{}, fmt.Errorf("direct install plugin %q not found in source", strings.TrimSpace(manifest.ID))
}
if PluginInstallType(resolved) != InstallTypeDirect {
return Plugin{}, fmt.Errorf("direct install plugin %q resolved as %q", strings.TrimSpace(manifest.ID), PluginInstallType(resolved))
}
return directPluginVersion(resolved, manifest.ID, manifest.Version)
}
func directPluginVersion(plugin Plugin, id string, version string) (Plugin, error) {View on GitHub (pinned to 78f0c4079e)
Solutions
- Set manifest.SourceURL to the registry base URL that serves the plugin
- Or set c.RegistryURL on the Client before calling the install API
- Or embed the artifact list (Install.Artifacts with URLs/checksums) directly in the manifest so no registry resolution is needed
Example fix
// before
client := pluginstore.Client{} // RegistryURL empty
plugin, err := client.ResolveDirect(ctx, manifest) // manifest.SourceURL == ""
// after
client := pluginstore.Client{RegistryURL: "https://plugins.example.com/registry.json"}
plugin, err := client.ResolveDirect(ctx, manifest) Defensive patterns
Strategy: validation
Validate before calling
func manifestResolvable(c pluginstore.Client, manifest Manifest) error {
if len(manifest.Plugin().Install.Artifacts) > 0 {
return nil // inline artifacts, no registry needed
}
if strings.TrimSpace(manifest.SourceURL) == "" && strings.TrimSpace(c.RegistryURL) == "" {
return errors.New("manifest has no artifacts and no source-url; set manifest.SourceURL or Client.RegistryURL")
}
return nil
} Try / catch
if err := client.ResolveDirect(ctx, manifest); err != nil {
if strings.Contains(err.Error(), "missing source-url") {
return errors.New("configuration incomplete: provide a registry URL")
}
} Prevention
- Always construct the Client through a factory that requires a non-empty RegistryURL
- Lint manifests at build time: either artifacts or source-url must be present
- Add a unit test asserting a zero-value Client fails fast with this exact message
When it happens
Trigger: Calling the manifest-based direct install path with a manifest that has no Install.Artifacts entries, while both manifest.SourceURL is blank/whitespace and the Client was constructed with an empty RegistryURL (zero-value Client or RegistryURL never set).
Common situations: Constructing pluginstore.Client{} via struct literal and forgetting to set RegistryURL; a hand-written manifest.json that omits the source-url field; trimming/removing the registry URL during config refactoring; tests using a zero-value client.
Related errors
- fetch direct install source: %w
- direct install plugin %q not found in source
- direct install plugin %q resolved as %q
- plugin_manifest_failed
- home ca fingerprint is required
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/7fb2d64a86514f3d.
Report an issue: GitHub.