{"record":{"id":"22b10f6c12ed98e1","repo":"beeradmoore/dlss-swapper","slug":"localrecord-was-null-when-attempting-to-extract-dll-from-zip","errorCode":null,"errorMessage":"LocalRecord was null when attempting to extract dll from zip.","messagePattern":"LocalRecord was null when attempting to extract dll from zip\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/Data/DLLManager.cs","lineNumber":1385,"sourceCode":"            GameAssetType.XeSS => \"libxess.dll\",\n            GameAssetType.XeSS_FG => \"libxess_fg.dll\",\n            GameAssetType.XeLL => \"libxell.dll\",\n            GameAssetType.XeSS_DX11 => \"libxess_dx11.dll\",\n            _ => string.Empty,\n        };\n    }\n\n    /// <summary>\n    /// This handles extracting of the DLL from both downloaded and imported zips (when imported matches the hash of one that could be downloaded)\n    /// </summary>\n    /// <param name=\"zipArchive\"></param>\n    /// <param name=\"dllRecord\"></param>\n    /// <exception cref=\"Exception\"></exception>\n    internal static void HandleExtractFromZip(ZipArchive zipArchive, DLLRecord dllRecord)\n    {\n        if (dllRecord.LocalRecord is null)\n        {\n            throw new Exception(\"LocalRecord was null when attempting to extract dll from zip.\");\n        }\n\n        var dllName = DLLManager.DllNameForGameAssetType(dllRecord.AssetType);\n        var entry = zipArchive.Entries.Single(x => x.Name.Equals(dllName, StringComparison.OrdinalIgnoreCase));\n        if (entry is null)\n        {\n            throw new Exception(\"Could not find dll in zip.\");\n        }\n        else\n        {\n            Storage.CreateDirectoryForFileIfNotExists(dllRecord.LocalRecord.ExpectedPath);\n            entry.ExtractToFile(dllRecord.LocalRecord.ExpectedPath, true);\n        }\n    }\n\n}\n","sourceCodeStart":1367,"sourceCodeEnd":1402,"githubUrl":"https://github.com/beeradmoore/dlss-swapper/blob/ab9b1e2d4bb04187c48fe58eeea06c2b0ea71fbb/src/Data/DLLManager.cs#L1367-L1402","documentation":"HandleExtractFromZip requires a populated DLLRecord.LocalRecord because the extraction target path (LocalRecord.ExpectedPath) comes from it; when LocalRecord is null the record has never had local installation state computed, so extraction cannot proceed and the library throws. This is an internal precondition/invariant failure in the download-and-extract pipeline.","triggerScenarios":"Calling HandleExtractFromZip (or the download/extract flow that leads to it) with a DLLRecord whose LocalRecord was never initialized — e.g. a record constructed without EnsureLocalRecord/initialization, or one built for an asset type the manager never registered locally.","commonSituations":"Manifest record deserialized without local state; calling extraction APIs directly on a fresh DLLRecord; asset type missing from the local records dictionary so lookup returned null.","solutions":["Initialize the DLLRecord's LocalRecord (expected path/registration) before extracting from the zip","Ensure the asset type is present in the manager's local records before invoking extraction","Guard the call site: skip or initialize records with null LocalRecord"],"exampleFix":"// before\nHandleExtractFromZip(zipArchive, dllRecord);\n// after\nif (dllRecord.LocalRecord is null)\n{\n    await dllRecord.InitializeLocalRecordAsync(); // or obtain record via manager lookup\n}\nHandleExtractFromZip(zipArchive, dllRecord);","handlingStrategy":"type-guard","validationCode":"// before extraction\nif (dllRecord.LocalRecord is null)\n    await dllRecord.InitializeAsync(); // or fetch from manager registry","typeGuard":"bool CanExtract(DLLRecord r) => r.LocalRecord?.ExpectedPath is not null;","tryCatchPattern":"try\n{\n    HandleExtractFromZip(zipArchive, dllRecord);\n}\ncatch (Exception ex) when (ex.Message.Contains(\"LocalRecord was null\"))\n{\n    logger.LogError(ex, \"Record {AssetType} not initialized; re-registering.\", dllRecord.AssetType);\n}","preventionTips":["Always construct DLLRecords through the manager so LocalRecord is initialized","Assert LocalRecord is non-null at record creation","Don't persist/reuse records across app restarts without re-initializing local state"],"tags":["null","invariant","zip-extract","dotnet"],"backgroundTag":"null-argument","analyzedSha":"ab9b1e2d4bb04187c48fe58eeea06c2b0ea71fbb","analyzedAt":"2026-09-15T05:25:32.979Z","contentChangedAt":"2026-09-15T05:25:32.979Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}