{"record":{"id":"f104fb528621aa69","repo":"router-for-me/CLIProxyAPI","slug":"github-release-manifest-requires-a-resolved-releas","errorCode":null,"errorMessage":"github-release manifest requires a resolved release","messagePattern":"github-release manifest requires a resolved release","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/manifest.go","lineNumber":57,"sourceCode":"}\n\nfunc ManifestFromPlugin(source Source, plugin Plugin) (Manifest, error) {\n\tif errValidate := ValidatePlugin(plugin); errValidate != nil {\n\t\treturn Manifest{}, errValidate\n\t}\n\tswitch PluginInstallType(plugin) {\n\tcase InstallTypeDirect:\n\t\tmanifest := manifestFromPlugin(source, plugin, Manifest{\n\t\t\tSchemaVersion: SchemaVersionV2,\n\t\t\tVersion:       strings.TrimSpace(plugin.Version),\n\t\t\tInstall:       NormalizeInstallPlan(plugin.Install),\n\t\t})\n\t\tif errValidate := manifest.Validate(); errValidate != nil {\n\t\t\treturn Manifest{}, errValidate\n\t\t}\n\t\treturn manifest, nil\n\tcase InstallTypeGitHubRelease:\n\t\treturn Manifest{}, fmt.Errorf(\"github-release manifest requires a resolved release\")\n\tdefault:\n\t\treturn Manifest{}, fmt.Errorf(\"unsupported install type %q\", plugin.Install.Type)\n\t}\n}\n\nfunc manifestFromPlugin(source Source, plugin Plugin, base Manifest) Manifest {\n\tbase.ID = strings.TrimSpace(plugin.ID)\n\tbase.Name = strings.TrimSpace(plugin.Name)\n\tbase.Description = strings.TrimSpace(plugin.Description)\n\tbase.Author = strings.TrimSpace(plugin.Author)\n\tbase.Logo = strings.TrimSpace(plugin.Logo)\n\tbase.Homepage = strings.TrimSpace(plugin.Homepage)\n\tbase.License = strings.TrimSpace(plugin.License)\n\tbase.Tags = append([]string(nil), plugin.Tags...)\n\tbase.SourceID = strings.TrimSpace(source.ID)\n\tbase.SourceName = strings.TrimSpace(source.Name)\n\tbase.SourceURL = strings.TrimSpace(source.URL)\n\treturn base","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/manifest.go#L39-L75","documentation":"Returned by ManifestFromPlugin (internal/pluginstore/manifest.go:56-57) when asked to build a manifest for a plugin whose install type is github-release. A github-release manifest carries fields (Version from the release, ReleaseTag) that only exist once a concrete release has been resolved from GitHub, so the un-resolved form is deliberately rejected rather than emitting an incomplete manifest.","triggerScenarios":"Calling ManifestFromPlugin(source, plugin) where PluginInstallType(plugin) == \"github-release\" (explicitly set, or defaulted when Install.Type is empty). The correct entry point for this case is ManifestFromRelease, which takes the fetched Release and is invoked after FetchLatestRelease/FetchReleaseByTag.","commonSituations":"Registry-driven code iterating plugins and calling ManifestFromPlugin uniformly, forgetting that release-type plugins must go through FetchLatestRelease first; porting call sites from a version of the API where manifests did not distinguish install types.","solutions":["Fetch a release first and use ManifestFromRelease(source, plugin, release)","If you want 'latest', call Client.FetchLatestRelease then ManifestFromRelease","Branch on PluginInstallType(plugin): direct -> ManifestFromPlugin, github-release -> ManifestFromRelease"],"exampleFix":"// before\nm, err := pluginstore.ManifestFromPlugin(src, plugin) // panics-free but errors for github-release plugins\n// after\nrelease, err := client.FetchLatestRelease(ctx, plugin)\nif err != nil { return err }\nm, err := pluginstore.ManifestFromRelease(src, plugin, release)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func manifestBuildableWithoutRelease(p pluginstore.Plugin) bool {\n    return pluginstore.PluginInstallType(p) == pluginstore.InstallTypeDirect\n}","tryCatchPattern":null,"preventionTips":["Branch on PluginInstallType before choosing ManifestFromPlugin vs ManifestFromRelease","For github-release plugins, always fetch the release first (FetchLatestRelease or FetchReleaseByTag)","Wrap manifest generation in a helper that dispatches per install type so call sites cannot pick the wrong constructor"],"tags":["pluginstore","manifest","api-misuse","github-release"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}