subhra74/xdm · error · IOException

Unable to read state:

Error message

Unable to read state: 

What it means

DownloadStateIO.LoadSingleSourceHTTPDownloaderState throws IOException("Unable to read state: <id>") when TransactedBinaryDataReader.Read completed but the deserializer (SingleSourceHTTPDownloaderStateFromBytes) produced a null state. This means the <id>.state file in Config.DataDir exists (or a transaction left no usable data) but could not yield a valid SingleSourceHTTPDownloaderState object — typically a corrupt, empty, or format-incompatible state file.

Solutions

  1. Treat the download as unrecoverable: delete <id>.state from Config.DataDir and start the download again.
  2. Back up the file first and inspect its size/contents; an empty or tiny file confirms corruption.
  3. If the file came from another XDM version, restore the state with the matching version or re-create the download.
  4. Check for leftover transaction artifacts in Config.DataDir and restore the last committed state file if present.

Example fix

// before
var state = DownloadStateIO.LoadSingleSourceHTTPDownloaderState(id); // throws IOException
// after
SingleSourceHTTPDownloaderState state;
try { state = DownloadStateIO.LoadSingleSourceHTTPDownloaderState(id); }
catch (IOException)
{
    DownloadStateIO.DeleteState(id); // discard corrupt state
    state = null; // fall back to starting a fresh download
}
Defensive patterns

Strategy: try-catch

Validate before calling

var path = Path.Combine(Config.DataDir, id + ".state");
if (!File.Exists(path) || new FileInfo(path).Length == 0)
{
    // state missing or empty — do not attempt load; start a fresh download
}

Type guard

static bool StateFileLooksValid(string dataDir, string id)
{
    var p = Path.Combine(dataDir, id + ".state");
    try { return File.Exists(p) && new FileInfo(p).Length > 0; }
    catch (IOException) { return false; }
}

Try / catch

SingleSourceHTTPDownloaderState state;
try { state = DownloadStateIO.LoadSingleSourceHTTPDownloaderState(id); }
catch (IOException ex)
{
    Log.Debug(ex, $"corrupt state {id}");
    DownloadStateIO.DeleteState(id);
    state = null; // fall back to a fresh download
}

Prevention

When it happens

Trigger: Calling DownloadStateIO.LoadSingleSourceHTTPDownloaderState(id) where the <id>.state file is empty, truncated, corrupted, or written by an incompatible binary format version, so SingleSourceHTTPDownloaderStateFromBytes returns null.

Common situations: App crash/kill mid-write (mitigated by transacted read but not fully); upgrading XDM across a state-format change; manual copying of state files between machines; disk corruption or partial file sync (Dropbox/OneDrive) of the data directory.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of subhra74/xdm@1ca5a25aae (2026-09-13). Data as JSON: /api/errors/be31478575a3d72a. Report an issue: GitHub.

Appendix: source

Thrown at app/XDM/XDM.Core/IO/DownloadStateIO.cs:72

#if NET35
                ms.CopyTo(stream);
#endif
            });
        }
    }

    public static class DownloadStateIO
    {
        public static SingleSourceHTTPDownloaderState LoadSingleSourceHTTPDownloaderState(string id)
        {
            SingleSourceHTTPDownloaderState? state = null;
            TransactedBinaryDataReader.Read($"{id}.state", Config.DataDir, r =>
            {
                state = SingleSourceHTTPDownloaderStateFromBytes(r);
            });
            if (state == null)
            {
                throw new IOException("Unable to read state: " + id);
            }
            return state;
        }

        private static SingleSourceHTTPDownloaderState SingleSourceHTTPDownloaderStateFromBytes(BinaryReader r)
        {
            var state = new SingleSourceHTTPDownloaderState
            {
                Id = r.ReadString(),
                TempDir = XDM.Messaging.StreamHelper.ReadString(r),
                FileSize = r.ReadInt64(),
                LastModified = DateTime.FromBinary(r.ReadInt64()),
                SpeedLimit = r.ReadInt32(),
                Url = new Uri(r.ReadString())
            };
            if (r.ReadBoolean())
            {
                XDM.Messaging.StreamHelper.ReadStateHeaders(r, out Dictionary<string, List<string>> headers);

View on GitHub (pinned to 1ca5a25aae)