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

  1. Set manifest.SourceURL to the registry base URL that serves the plugin
  2. Or set c.RegistryURL on the Client before calling the install API
  3. 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

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


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