{"record":{"id":"3b2826128d901c8c","repo":"Devolutions/UniGetUI","slug":"no-compatible-installer-file-found-in-productinfo","errorCode":null,"errorMessage":"No compatible installer file found in productinfo for architecture '{targetArch}'","messagePattern":"No compatible installer file found in productinfo for architecture '(.+?)'","errorType":"exception","errorClass":"KeyNotFoundException","httpStatus":null,"severity":"error","filePath":"src/Shared/AutoUpdater.Helpers.cs","lineNumber":69,"sourceCode":"\n        match ??= files.FirstOrDefault(file =>\n            file.Type.Equals(\"exe\", StringComparison.OrdinalIgnoreCase)\n            && file.Arch.Equals(\"Any\", StringComparison.OrdinalIgnoreCase)\n        );\n\n        match ??= files.FirstOrDefault(file =>\n            file.Type.Equals(\"msi\", StringComparison.OrdinalIgnoreCase)\n            && file.Arch.Equals(targetArch, StringComparison.OrdinalIgnoreCase)\n        );\n\n        match ??= files.FirstOrDefault(file =>\n            file.Type.Equals(\"msi\", StringComparison.OrdinalIgnoreCase)\n            && file.Arch.Equals(\"Any\", StringComparison.OrdinalIgnoreCase)\n        );\n\n        if (match is null)\n        {\n            throw new KeyNotFoundException(\n                $\"No compatible installer file found in productinfo for architecture '{targetArch}'\"\n            );\n        }\n\n        return match;\n    }\n\n    internal static Version ParseVersionOrFallback(string rawVersion, Version fallbackVersion)\n    {\n        if (Version.TryParse(rawVersion, out Version? parsed))\n        {\n            return CoreTools.NormalizeVersionForComparison(parsed);\n        }\n\n        string sanitized = rawVersion.Trim().TrimStart('v', 'V');\n        if (Version.TryParse(sanitized, out parsed))\n        {\n            return CoreTools.NormalizeVersionForComparison(parsed);","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/Devolutions/UniGetUI/blob/9b1d7d0eab91620fc15c86b36676532095936ae3/src/Shared/AutoUpdater.Helpers.cs#L51-L87","documentation":"SelectInstallerFile in AutoUpdaterHelpers picks an installer from the product-info files list. It searches in order: exe+targetArch, exe+Any, msi+targetArch, msi+Any. targetArch is derived from RuntimeInformation.ProcessArchitecture (arm64 -> \"arm64\", everything else -> \"x64\"). If none of the four combinations yield a match, a KeyNotFoundException is thrown. The match is case-insensitive ordinal on both Type and Arch.","triggerScenarios":"The product-info manifest's files array contains no ProductInfoFile whose Type is \"exe\" or \"msi\" AND whose Arch equals the running architecture or \"Any\". For example the manifest lists only \"msix\" or \"zip\" types, or only lists \"arm64\" while RuntimeInformation.ProcessArchitecture is X64 (or vice-versa). A typo such as Arch=\"x86_64\" or Type=\"EXE \" (trailing space) also produces no match because Equals is ordinal case-insensitive but does not trim.","commonSituations":"A release publishes only a zip/msix asset and omits exe/msi entries. A new architecture port (e.g. arm64) ships but the manifest was not updated with a matching installer row. The Arch column uses a different naming convention (\"amd64\" instead of \"x64\", \"aarch64\" instead of \"arm64\"). The manifest file is empty or the deserialized files list came back empty due to a schema change.","solutions":["Add an exe or msi ProductInfoFile entry to the product-info manifest whose Arch is the exact target architecture (\"x64\" or \"arm64\") or \"Any\".","Verify the Arch strings use the canonical values \"x64\", \"arm64\", or \"Any\" — not \"amd64\", \"aarch64\", \"x86_64\".","If the build target changed (e.g. new arm64 support), update every release manifest to include an installer for that architecture.","Inspect the files list before calling SelectInstallerFile to confirm it is populated and that Type/Arch values are spelled correctly."],"exampleFix":"// before: manifest only has a zip\nfiles: [ { Type: \"zip\", Arch: \"x64\", Url: \"...\" } ]\n// after: add an exe or msi with the canonical Arch\nfiles: [\n  { Type: \"zip\", Arch: \"x64\", Url: \"...\" },\n  { Type: \"exe\", Arch: \"x64\", Url: \"...\" }\n]","handlingStrategy":"validation","validationCode":"string targetArch = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ? \"arm64\" : \"x64\";\nbool hasMatch = files.Any(f =>\n    (f.Type.Equals(\"exe\", StringComparison.OrdinalIgnoreCase) || f.Type.Equals(\"msi\", StringComparison.OrdinalIgnoreCase))\n    && (f.Arch.Equals(targetArch, StringComparison.OrdinalIgnoreCase) || f.Arch.Equals(\"Any\", StringComparison.OrdinalIgnoreCase)));\nif (!hasMatch)\n    Logger.Warn($\"No installer for architecture {targetArch}; available: {string.Join(\", \", files.Select(f => $\"{f.Type}/{f.Arch}\"))}\");","typeGuard":"static bool HasInstallerForArch(List<ProductInfoFile> files, string arch) =>\n    files.Any(f => (f.Type.Equals(\"exe\", StringComparison.OrdinalIgnoreCase)\n                 || f.Type.Equals(\"msi\", StringComparison.OrdinalIgnoreCase))\n                 && (f.Arch.Equals(arch, StringComparison.OrdinalIgnoreCase)\n                     || f.Arch.Equals(\"Any\", StringComparison.OrdinalIgnoreCase)));","tryCatchPattern":"try { var installer = AutoUpdaterHelpers.SelectInstallerFile(files); }\ncatch (KeyNotFoundException ex) { Logger.Error(ex); /* report missing installer to user */ }","preventionTips":["Always include an exe or msi entry with Arch=\"Any\" as a fallback in release manifests.","Use the canonical Arch strings \"x64\" and \"arm64\" — never \"amd64\" or \"aarch64\".","Validate the manifest's files list in CI before publishing a release."],"tags":["autoupdater","architecture","installer","manifest"],"backgroundTag":null,"analyzedSha":"9b1d7d0eab91620fc15c86b36676532095936ae3","analyzedAt":"2026-08-13T12:19:18.278Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}