Flow-Launcher/Flow.Launcher · error · FileNotFoundException
The zip file does not contain a plugin.json file.
Error message
The zip file does not contain a plugin.json file.
What it means
Thrown as FileNotFoundException when ZipFile.OpenRead(filePath) succeeds but no archive entry has the exact name 'plugin.json' (case-sensitive Name match). It is caught immediately by the surrounding try/catch which logs 'Failed to validate zip file', shows a localized error, and returns — so this is a soft, user-facing failure rather than a crash.
Source
Thrown at Flow.Launcher.Core/Plugin/PluginInstaller.cs:118
PublicApi.Instance.ShowMsg(
Localize.installbtn(),
Localize.InstallSuccessNoRestart(newPlugin.Name));
}
}
/// <summary>
/// Installs a plugin from a local zip file and restarts the application if required by settings. Validates the zip and prompts user for confirmation.
/// </summary>
/// <param name="filePath">The path to the plugin zip file.</param>
/// <returns>A Task representing the asynchronous install operation.</returns>
public static async Task InstallPluginAndCheckRestartAsync(string filePath)
{
UserPlugin plugin;
try
{
using ZipArchive archive = ZipFile.OpenRead(filePath);
var pluginJsonEntry = archive.Entries.FirstOrDefault(x => x.Name == "plugin.json") ??
throw new FileNotFoundException("The zip file does not contain a plugin.json file.");
using Stream stream = pluginJsonEntry.Open();
plugin = JsonSerializer.Deserialize<UserPlugin>(stream);
plugin.IcoPath = "Images\\zipfolder.png";
plugin.LocalInstallPath = filePath;
}
catch (Exception e)
{
PublicApi.Instance.LogException(ClassName, "Failed to validate zip file", e);
PublicApi.Instance.ShowMsgError(Localize.ZipFileNotHavePluginJson());
return;
}
if (PublicApi.Instance.PluginModified(plugin.ID))
{
PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name),
Localize.pluginModifiedAlreadyMessage());
return;View on GitHub (pinned to 7fc63b07bb)
Solutions
- Open the .zip and confirm plugin.json exists at the archive root (not nested).
- Ensure the manifest is named exactly plugin.json (lowercase).
- Re-download the plugin from its official source in case the archive is a non-plugin zip.
- If you are the plugin author, adjust your build/release script to put plugin.json at the zip root.
Example fix
// before — only matches the exact root-level name
var pluginJsonEntry = archive.Entries.FirstOrDefault(x => x.Name == "plugin.json")
?? throw new FileNotFoundException("The zip file does not contain a plugin.json file.");
// after — search any depth and report what was found
var pluginJsonEntry = archive.Entries.FirstOrDefault(x =>
string.Equals(x.Name, "plugin.json", StringComparison.OrdinalIgnoreCase));
if (pluginJsonEntry is null)
{
PublicApi.Instance.ShowMsgError($"No plugin.json found. Entries: {string.Join(", ", archive.Entries.Select(e => e.FullName))}");
return;
} Defensive patterns
Strategy: validation
Validate before calling
using var archive = ZipFile.OpenRead(filePath);
bool hasPluginJson = archive.Entries.Any(e =>
string.Equals(e.Name, "plugin.json", StringComparison.OrdinalIgnoreCase));
if (!hasPluginJson) { ShowMsgError("plugin.json missing at zip root"); return; } Type guard
null
Try / catch
try { plugin = JsonSerializer.Deserialize<UserPlugin>(stream); }
catch (FileNotFoundException ex) when (ex.Message.Contains("plugin.json"))
{ PublicApi.Instance.ShowMsgError(Localize.ZipFileNotHavePluginJson()); return; } Prevention
- When packaging a plugin, flatten the directory so plugin.json sits at the zip root.
- Always name the manifest exactly plugin.json (lowercase).
- After zipping, open the archive and confirm the manifest's location.
- Download plugins only from the official manifest to avoid non-plugin zips.
When it happens
Trigger: User selects a .zip that is not a Flow Launcher plugin (random archive, wrong packaging); plugin.json is present but inside a nested subfolder so its entry Name differs; the file is named Plugin.json (different case) on a case-sensitive packaging; the zip is corrupt and entries aren't enumerable as expected.
Common situations: Developers packaging their plugin and forgetting to flatten the directory structure so plugin.json lands under a subfolder; users trying to install a zip downloaded from the wrong source; plugin built on a case-sensitive system that renamed the manifest.
Related errors
- Plugin {newPlugin.ID} zip file not found at {filePath}
- Invalid SVG dimensions: Height must be greater than zero in
- DoublePinyinSchema '{schemaKey}' is invalid or double pinyin
- Invalid corner type
- Source directory does not exist or could not be found: {sour
AI-assisted analysis of Flow-Launcher/Flow.Launcher@7fc63b07bb (2026-08-13).
Data as JSON: /api/errors/804a99ff8b7833cd.
Report an issue: GitHub.