BornToBeRoot/NETworkManager · error · Exception

string.Join("; ", ps.Streams.Error)

Error message

string.Join("; ", ps.Streams.Error)

What it means

SetRuleEnabledAsync runs Enable-NetFirewallRule or Disable-NetFirewallRule in a shared PowerShell runspace. If the cmdlet writes any records to the error stream, the method wraps them (joined with "; ") in a generic System.Exception and throws. The message is the raw PowerShell error text, typically a non-terminating error such as a rule name that does not exist.

Solutions

  1. Run the application elevated (as administrator) before calling firewall APIs.
  2. Re-fetch the rule list with GetRulesAsync and retry with a fresh FirewallRule whose Id still exists.
  3. Catch the exception and surface the joined PowerShell error text to the user, since it contains the actual NetSecurity cmdlet error.
  4. Verify the rule name manually with PowerShell: Enable-NetFirewallRule -Name '<rule.Id>' to reproduce the error text.

Example fix

// before
await Firewall.SetRuleEnabledAsync(staleRule, true);
// after
var rules = await Firewall.GetRulesAsync();
var fresh = rules.FirstOrDefault(r => r.Id == staleRule.Id) ?? throw new InvalidOperationException($"Rule {staleRule.Id} no longer exists");
await Firewall.SetRuleEnabledAsync(fresh, true);
Defensive patterns

Strategy: try-catch

Validate before calling

// refresh and verify the rule exists before toggling
var rules = await Firewall.GetRulesAsync();
if (!rules.Any(r => r.Id == rule.Id)) throw new InvalidOperationException($"Firewall rule '{rule.Id}' no longer exists");

Type guard

static bool RuleExists(IEnumerable<FirewallRule> rules, FirewallRule rule) => rules.Any(r => r != null && !string.IsNullOrEmpty(r.Id) && r.Id == rule.Id);

Try / catch

try
{
    await Firewall.SetRuleEnabledAsync(rule, enabled);
}
catch (Exception ex) when (ex.Message.Contains("NetFirewallRule"))
{
    Log.Warn($"Firewall toggle failed: {ex.Message}");
    // surface ex.Message (PowerShell error text) to the user
}

Prevention

When it happens

Trigger: Calling SetRuleEnabledAsync(rule, enabled) when rule.Id does not match an existing firewall rule name (e.g. the rule was deleted externally or the Id is stale from a cached GetRulesAsync snapshot), or when the process lacks admin rights so the NetSecurity cmdlet fails.

Common situations: App not running elevated (NetFirewall cmdlets require administrator); a stale FirewallRule object after the rule was removed by another tool or group policy refresh; localized Windows error text making the message hard to parse.

Related errors


AI-assisted analysis of BornToBeRoot/NETworkManager@2780d65469 (2026-09-12). Data as JSON: /api/errors/72758e772c38177f. Report an issue: GitHub.

Appendix: source

Thrown at Source/NETworkManager.Models/Firewall/Firewall.cs:198

    /// </param>
    /// <exception cref="Exception">
    /// Thrown when the PowerShell pipeline reports one or more errors.
    /// </exception>
    public static async Task SetRuleEnabledAsync(FirewallRule rule, bool enabled)
    {
        await RunspaceLock.WaitAsync();
        try
        {
            await Task.Run(() =>
            {
                using var ps = SMA.PowerShell.Create();
                ps.Runspace = SharedRunspace;

                ps.AddScript($@"{(enabled ? "Enable" : "Disable")}-NetFirewallRule -Name '{rule.Id}'");
                ps.Invoke();

                if (ps.Streams.Error.Count > 0)
                    throw new Exception(string.Join("; ", ps.Streams.Error));
            });
        }
        finally
        {
            RunspaceLock.Release();
        }
    }

    /// <summary>
    /// Permanently removes the given <paramref name="rule"/> by running
    /// <c>Remove-NetFirewallRule</c> against the rule's internal <see cref="FirewallRule.Id"/>.
    /// </summary>
    /// <param name="rule">
    /// The firewall rule to delete.
    /// </param>
    /// <exception cref="Exception">
    /// Thrown when the PowerShell pipeline reports one or more errors.
    /// </exception>

View on GitHub (pinned to 2780d65469)