router-for-me/CLIProxyAPI · error

install type %q is not direct

Error message

install type %q is not direct

What it means

SelectArtifact picks the direct-download artifact matching the runtime GOOS/GOARCH from an InstallPlan. It throws 'install type %q is not direct' when the normalized plan's Type is anything other than "direct" (InstallTypeDirect). The function is the artifact selector for the direct install strategy only; GitHub-release-based or source-install plans are rejected up front.

Source

Thrown at internal/pluginstore/direct.go:16

package pluginstore

import (
	"context"
	"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
	}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Route the plan by type first: only call SelectArtifact when plan.Type == "direct"; use the GitHub release flow for github-type plans.
  2. Check the plugin manifest's install.type field for typos and set it to "direct" if the plugin publishes plain platform binaries.
  3. Log plan.Type before calling SelectArtifact to see the exact non-direct value being passed.

Example fix

// before
artifact, err := pluginstore.SelectArtifact(plan, runtime.GOOS, runtime.GOARCH)
// panics path for github-type plans -> 'install type "github" is not direct'

// after
switch plan.Type {
case pluginstore.InstallTypeDirect:
    artifact, err = pluginstore.SelectArtifact(plan, runtime.GOOS, runtime.GOARCH)
case pluginstore.InstallTypeGitHub:
    artifact, err = selectGitHubArtifact(ctx, client, plan, runtime.GOOS, runtime.GOARCH)
default:
    return fmt.Errorf("unsupported install type %q", plan.Type)
}
Defensive patterns

Strategy: type-guard

Validate before calling

plan = pluginstore.NormalizeInstallPlan(plan)
if plan.Type != pluginstore.InstallTypeDirect {
    return fmt.Errorf("plan type %q needs a non-direct installer", plan.Type)
}

Type guard

func isDirectPlan(plan pluginstore.InstallPlan) bool {
    return pluginstore.NormalizeInstallPlan(plan).Type == pluginstore.InstallTypeDirect
}

Prevention

When it happens

Trigger: Calling SelectArtifact(plan, goos, goarch) with plan.Type set to a non-direct value such as "github", "source", or the empty string after NormalizeInstallPlan. This happens when a plugin manifest declares a non-direct install strategy but the caller routes it through the direct-download code path.

Common situations: Plugin index entries whose manifest uses a github release strategy being passed to the direct installer; typos in the `type` field of a plugin manifest (e.g. "Direct" is not normalized if unsupported); routing logic that does not switch on plan.Type before calling SelectArtifact.

Related errors


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