{"record":{"id":"e62a623a9b45c323","repo":"router-for-me/CLIProxyAPI","slug":"direct-install-plugin-q-version-q-resolved-as-q","errorCode":null,"errorMessage":"direct install plugin %q version %q resolved as %q","messagePattern":"direct install plugin %q version %q resolved as %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/install.go","lineNumber":233,"sourceCode":"\t\tplugin.Version = version\n\t\tplugin.Install = NormalizeInstallPlan(plugin.Install)\n\t\tplugin.Install.Type = InstallTypeDirect\n\t\tif errPlan := ValidateInstallPlan(plugin.Install); errPlan != nil {\n\t\t\treturn Plugin{}, fmt.Errorf(\"direct install plugin %q version %q: %w\", id, version, errPlan)\n\t\t}\n\t\treturn plugin, nil\n\t}\n\tfor _, candidate := range plugin.Versions {\n\t\tif normalizeVersion(candidate.Version) != version {\n\t\t\tcontinue\n\t\t}\n\t\tplugin.Version = version\n\t\tplugin.Install = NormalizeInstallPlan(candidate.Install)\n\t\tif plugin.Install.Type == \"\" {\n\t\t\tplugin.Install.Type = InstallTypeDirect\n\t\t}\n\t\tif plugin.Install.Type != InstallTypeDirect {\n\t\t\treturn Plugin{}, fmt.Errorf(\"direct install plugin %q version %q resolved as %q\", id, version, plugin.Install.Type)\n\t\t}\n\t\tif errPlan := ValidateInstallPlan(plugin.Install); errPlan != nil {\n\t\t\treturn Plugin{}, fmt.Errorf(\"direct install plugin %q version %q: %w\", id, version, errPlan)\n\t\t}\n\t\treturn plugin, nil\n\t}\n\treturn Plugin{}, fmt.Errorf(\"direct install plugin %q version %q not found in source\", id, version)\n}\n\nfunc InstallArchive(archiveData []byte, plugin Plugin, options InstallOptions) (InstallResult, error) {\n\toptions = normalizeInstallOptions(options)\n\tid := strings.TrimSpace(plugin.ID)\n\tif !validPluginID(id) {\n\t\treturn InstallResult{}, fmt.Errorf(\"invalid plugin id %q\", plugin.ID)\n\t}\n\tversion := normalizeVersion(plugin.Version)\n\tif !validPluginVersion(version) {\n\t\treturn InstallResult{}, fmt.Errorf(\"invalid plugin version %q\", plugin.Version)","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/install.go#L215-L251","documentation":"Returned from directPluginVersion when the requested version matches one of the plugin's historical entries in plugin.Versions, but that candidate's install plan (after NormalizeInstallPlan, with empty type defaulted to direct) resolves to a non-direct install type. Per-version install metadata overrides the plugin-level type, and a non-direct candidate cannot be used for direct install.","triggerScenarios":"Requested version found only in plugin.Versions with install.type explicitly set to \"github-release\" (or another type) while the caller asked for direct install; mixed-type version history in the registry.","commonSituations":"Older versions of a plugin published via GitHub releases while newer ones are direct; registry migration that left legacy version entries with the old install type; requesting a pinned legacy version from a plugin that has since moved to direct distribution.","solutions":["Request a version whose registry entry is published with direct install type","Update the registry's version entry to install type \"direct\" with direct artifacts","Use the install API matching that version's published type"],"exampleFix":"// before\nmanifest.Version = \"0.9.0\" // registry: versions[0.9.0].install.type = \"github-release\"\nplugin, err := client.ResolveDirect(ctx, manifest)\n\n// after\nmanifest.Version = \"1.0.0\" // direct-type version\nplugin, err := client.ResolveDirect(ctx, manifest)","handlingStrategy":"validation","validationCode":"func versionIsDirectType(plugin pluginstore.Plugin, version string) bool {\n    v := strings.TrimPrefix(strings.TrimPrefix(strings.TrimSpace(version), \"v\"), \"V\")\n    for _, cand := range plugin.Versions {\n        if strings.TrimPrefix(strings.TrimSpace(cand.Version), \"v\") == v {\n            return pluginstore.PluginInstallType(pluginstore.Plugin{Install: cand.Install}) == pluginstore.InstallTypeDirect || strings.TrimSpace(string(cand.Install.Type)) == \"\"\n        }\n    }\n    return false\n}","typeGuard":"func isVersionTypeMismatch(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"version\") && strings.Contains(err.Error(), \"resolved as\")\n}","tryCatchPattern":"if err != nil {\n    if isVersionTypeMismatch(err) {\n        // pick another version or the matching install path\n    }\n}","preventionTips":["Keep install type consistent across all published versions of a plugin","When migrating distribution models, re-publish old versions under the new type","Expose available versions and their types to version-pinning users"],"tags":["go","plugin-store","registry","install-type","version"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}