router-for-me/CLIProxyAPI · error

artifact not found for %s/%s

Error message

artifact not found for %s/%s

What it means

SelectArtifact iterates the plan's normalized artifact list looking for one whose GOOS and GOARCH match the supplied values. It throws 'artifact not found for %s/%s' when no artifact entry covers the caller's platform pair (e.g. linux/arm64). Both inputs are normalized first, so the error text shows the canonical GOOS/GOARCH that failed to match.

Source

Thrown at internal/pluginstore/direct.go:23

	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strings"
)

func SelectArtifact(plan InstallPlan, goos string, goarch string) (Artifact, error) {
	plan = NormalizeInstallPlan(plan)
	goos = normalizeGOOS(goos)
	goarch = normalizeGOARCH(goarch)
	if plan.Type != InstallTypeDirect {
		return Artifact{}, fmt.Errorf("install type %q is not direct", plan.Type)
	}
	for _, artifact := range plan.Artifacts {
		if artifact.GOOS == goos && artifact.GOARCH == goarch {
			return artifact, nil
		}
	}
	return Artifact{}, fmt.Errorf("artifact not found for %s/%s", goos, goarch)
}

func (c Client) DownloadArtifact(ctx context.Context, artifact Artifact) ([]byte, error) {
	artifact = NormalizeInstallPlan(InstallPlan{Type: InstallTypeDirect, Artifacts: []Artifact{artifact}}).Artifacts[0]
	if errValidate := ValidateArtifact(artifact); errValidate != nil {
		return nil, errValidate
	}
	maxSize := int64(0)
	if artifact.Size > 0 {
		maxSize = artifact.Size
	}
	data, errDownload := c.get(ctx, artifact.URL, "application/octet-stream", RequestKindArtifact, maxSize)
	if errDownload != nil {
		return nil, errDownload
	}
	if maxSize > 0 && int64(len(data)) > maxSize {
		return nil, fmt.Errorf("artifact exceeds declared size")
	}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Inspect the plugin manifest's artifacts list and confirm it contains an entry for your runtime.GOOS/runtime.GOARCH.
  2. If you control the plugin, publish and declare a binary for the missing platform (e.g. add a linux/arm64 artifact).
  3. If running under emulation, pass the goos/goarch of the binary you actually want (e.g. amd64 under Rosetta) rather than the host pair.
  4. Fall back to a source build path for the plugin if one is available.

Example fix

// before
artifact, err := pluginstore.SelectArtifact(plan, runtime.GOOS, runtime.GOARCH)
// on linux/arm64: 'artifact not found for linux/arm64'

// after
supported := map[string]bool{}
for _, a := range plan.Artifacts {
    supported[a.GOOS+"/"+a.GOARCH] = true
}
if !supported[runtime.GOOS+"/"+runtime.GOARCH] {
    return fmt.Errorf("plugin does not support %s/%s; supported: %v", runtime.GOOS, runtime.GOARCH, supported)
}
artifact, err := pluginstore.SelectArtifact(plan, runtime.GOOS, runtime.GOARCH)
Defensive patterns

Strategy: validation

Validate before calling

supported := map[string]bool{}
for _, a := range pluginstore.NormalizeInstallPlan(plan).Artifacts {
    supported[a.GOOS+"/"+a.GOARCH] = true
}
if !supported[runtime.GOOS+"/"+runtime.GOARCH] {
    return fmt.Errorf("plugin lacks %s/%s build; have %v", runtime.GOOS, runtime.GOARCH, supported)
}

Type guard

func planSupports(plan pluginstore.InstallPlan, goos, goarch string) bool {
    for _, a := range pluginstore.NormalizeInstallPlan(plan).Artifacts {
        if a.GOOS == goos && a.GOARCH == goarch {
            return true
        }
    }
    return false
}

Prevention

When it happens

Trigger: Calling SelectArtifact with a GOOS/GOARCH pair not present in plan.Artifacts — e.g. the plugin publishes darwin/amd64, linux/amd64, windows/amd64 but the caller runs linux/arm64; or an artifacts list that is empty after normalization.

Common situations: Installing a plugin on Apple Silicon (darwin/arm64) when the publisher only shipped amd64 builds; running on rarer platforms (freebsd, linux/386); a manifest that lists artifacts without goos/goarch fields so nothing matches; passing raw runtime values like "darwin" with an unexpected casing (normalization usually fixes this, so a genuine gap is the remaining cause).

Related errors


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