{"record":{"id":"986a1e27ba1cdff1","repo":"TechnitiumSoftware/DnsServer","slug":"hourlystats-format-is-invalid","errorCode":null,"errorMessage":"HourlyStats format is invalid.","messagePattern":"HourlyStats format is invalid\\.","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"warning","filePath":"DnsServerCore/Dns/StatsManager.cs","lineNumber":1745,"sourceCode":"\n            #region constructor\n\n            public HourlyStats()\n            {\n                _hourStat = new StatCounter();\n                _hourStat.Lock();\n\n                for (int i = 0; i < _minuteStats.Length; i++)\n                {\n                    _minuteStats[i] = new StatCounter();\n                    _minuteStats[i].Lock();\n                }\n            }\n\n            public HourlyStats(BinaryReader bR)\n            {\n                if (Encoding.ASCII.GetString(bR.BaseStream.ReadExactly(2)) != \"HS\") //format\n                    throw new InvalidDataException(\"HourlyStats format is invalid.\");\n\n                byte version = bR.ReadByte();\n                switch (version)\n                {\n                    case 1:\n                        _hourStat = new StatCounter();\n                        _hourStat.Lock();\n\n                        for (int i = 0; i < _minuteStats.Length; i++)\n                        {\n                            _minuteStats[i] = new StatCounter(bR);\n                            _hourStat.Merge(_minuteStats[i]);\n                        }\n\n                        break;\n\n                    default:\n                        throw new InvalidDataException(\"HourlyStats version not supported.\");","sourceCodeStart":1727,"sourceCodeEnd":1763,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/StatsManager.cs#L1727-L1763","documentation":"Thrown by the HourlyStats(BinaryReader) constructor when the first two ASCII bytes of the stream are not 'HS'. The magic header is a format guard read before the version byte: it confirms the stream is actually an hourly .stat file and not a corrupt or foreign file. Failing here means the file is not an HourlyStats file at all.","triggerScenarios":"Opening a file in the stats folder as an HourlyStats stream when its first two bytes are not 0x48 0x53 ('HS'). Caused by a truncated/zero-byte file, a corrupt file, a non-stat file placed in the stats folder, or a partially-written file from a crash.","commonSituations":"Crash during stats write leaves a zero-length or partial .stat; an external process writes a file into the stats directory; disk corruption; copying unrelated files into the stats folder.","solutions":["Delete the offending .stat file (named yyyyMMddHH.stat) so the hour is treated as empty and re-accumulated going forward.","Verify the stats folder contains only files written by the server; remove foreign files.","Ensure the server is stopped before backing up or copying stats files to avoid catching a half-written file."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Skip non-HourlyStats files when scanning the stats folder.\nusing var fs = File.OpenRead(path);\nSpan<byte> magic = stackalloc byte[2];\nif (fs.Read(magic) != 2 || magic[0] != (byte)'H' || magic[1] != (byte)'S')\n    continue; // not an HourlyStats file; ignore","typeGuard":null,"tryCatchPattern":"try { hourly = new HourlyStats(bR); }\ncatch (InvalidDataException ex) when (ex.Message == \"HourlyStats format is invalid.\")\n{ hourly = HourlyStats.Empty; /* treat as empty hour */ }","preventionTips":["Stop the server before backing up the stats folder to avoid half-written files.","Keep foreign files out of the stats directory.","Delete zero-byte or partial .stat files found in the stats folder."],"tags":["stats","hourly-stats","serialization","deserialization","corruption"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}