netchx/netch · warning · FormatException

Not a valid txt mode that begins with meta line

Error message

Not a valid txt mode that begins with meta line

What it means

ReadTxtMode parses legacy plain-text mode files. The meta header must be the first line and must start with '#'; the code reads ls.First().First() and throws FormatException if that character is not '#'. The header is then parsed as ls[0][1..].Split(',') where element 1 selects the mode type (0=Redirector, 1/2=TunMode, 6=ShareMode). Caveat: an empty file or empty first line throws a different exception (InvalidOperationException on ls.First(), or a null/null-ref on .First()) before this check is reached. ModeService.LoadCore catches general Exceptions here and logs a warning, skipping the file, so this is non-fatal to startup.

Source

Thrown at Netch/Utils/ModeHelper.cs:56

        var mode = JsonSerializer.Deserialize<Mode>(fs, JsonSerializerOptions) ?? throw new ArgumentNullException();
        mode.FullName = file;
        return mode;
    }

    public static void WriteFile(this Mode mode)
    {
        using var fs = new FileStream(mode.FullName, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
        JsonSerializer.Serialize(fs, mode, JsonSerializerOptions);
    }

    private static Mode ReadTxtMode(string file)
    {
        Mode mode;
        var ls = File.ReadAllLines(file);
        string modeTypeNum;

        if (ls.First().First() != '#')
            throw new FormatException("Not a valid txt mode that begins with meta line");

        var heads = ls[0][1..].Split(",", StringSplitOptions.TrimEntries);
        switch (modeTypeNum = heads.ElementAtOrDefault(1) ?? "0")
        {
            case "0":
                mode = new Redirector { FullName = file };
                break;
            case "1":
            case "2":
                mode = new TunMode { FullName = file };
                break;
            case "6":
                mode = new ShareMode { FullName = file };
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

View on GitHub (pinned to 9d99eb1c5a)

Solutions

  1. Ensure the file's first line is a meta header like '#My Mode,0' (remark, then type number).
  2. Move non-mode txt files out of the mode/ directory.
  3. Fix or delete the malformed file; ModeService logs the offending filename as a warning and skips it.
  4. If authoring a new txt mode, follow the '#Remark,Type' header convention exactly.

Example fix

// before
if (ls.First().First() != '#')
    throw new FormatException("Not a valid txt mode that begins with meta line");
// after - guard empty lines and give a clearer, file-specific error
if (ls.Length == 0 || ls[0].Length == 0 || ls[0][0] != '#')
    throw new FormatException($"'{file}' must start with a '#Remark,Type' meta line");
Defensive patterns

Strategy: validation

Validate before calling

var lines = File.ReadAllLines(file);
if (lines.Length == 0 || lines[0].Length == 0 || lines[0][0] != '#')
{
    Log.Warning("Skipping non-mode txt file {File}", file);
    return;
}

Type guard

static bool IsTxtModeHeader(IReadOnlyList<string> lines)
    => lines.Count > 0 && lines[0].Length > 0 && lines[0][0] == '#';

Try / catch

try { Global.Modes.Add(ModeHelper.LoadMode(file)); }
catch (FormatException ex)
{
    Log.Warning(ex, "Load mode \"{FileName}\" failed", file);
    // ModeService.LoadCore already swallows this gracefully
}

Prevention

When it happens

Trigger: A .txt file in the mode/ tree whose first line lacks a leading '#'; an empty txt file (throws before this line); a non-mode txt file placed in mode/; a file whose first character is a BOM or whitespace rather than '#'.

Common situations: User drops an arbitrary notes/README txt into mode/; a corrupted mode file; a hand-edited file missing the header; an editor saving without the meta line.

Related errors


AI-assisted analysis of netchx/netch@9d99eb1c5a (2026-08-13). Data as JSON: /api/errors/9f786b7d1772c6a3. Report an issue: GitHub.