{"record":{"id":"dece4d9a986c01a1","repo":"JustArchiNET/ArchiSteamFarm","slug":"last-assetid-is-null","errorCode":null,"errorMessage":"last_assetid is null!","messagePattern":"last_assetid is null!","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"warning","filePath":"ArchiSteamFarm/Steam/Integration/ArchiHandler.cs","lineNumber":328,"sourceCode":"\t\t\t\t}\n\n\t\t\t\tforeach (CEcon_Asset? asset in response.Body.assets.Where(asset => assetIDs.Add(asset.assetid))) {\n\t\t\t\t\tInventoryDescription? description = descriptions.GetValueOrDefault((asset.classid, asset.instanceid));\n\n\t\t\t\t\t// Extra bulletproofing against Steam showing us middle finger\n\t\t\t\t\tif ((tradableOnly && (description?.Tradable != true)) || (marketableOnly && (description?.Marketable != true))) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tyield return new Asset(asset, description);\n\t\t\t\t}\n\n\t\t\t\tif (!response.Body.more_items) {\n\t\t\t\t\tyield break;\n\t\t\t\t}\n\n\t\t\t\tif (response.Body.last_assetid == 0) {\n\t\t\t\t\tthrow new NotSupportedException(Strings.FormatErrorObjectIsNull(nameof(response.Body.last_assetid)));\n\t\t\t\t}\n\n\t\t\t\trequest.start_assetid = response.Body.last_assetid;\n\t\t\t}\n\t\t} finally {\n\t\t\tInventorySemaphore.Release();\n\t\t}\n\t}\n\n\t[PublicAPI]\n\tpublic async Task<Dictionary<uint, string>?> GetOwnedGames(ulong steamID) {\n\t\tif ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(steamID));\n\t\t}\n\n\t\tif (Client == null) {\n\t\t\tthrow new InvalidOperationException(nameof(Client));\n\t\t}","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/JustArchiNET/ArchiSteamFarm/blob/fe57c4129f4ce02d452318cabc525eb49a087255/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs#L310-L346","documentation":"Pagination guard in GetMyInventoryAsync: when response.Body.more_items is true (Steam says there is another page) but response.Body.last_assetid is 0, ASF cannot form the next request's start_assetid and throws a NotSupportedException ('last_assetid is null!'). It refuses to silently loop or skip items.","triggerScenarios":"Steam marks the inventory page as having more items (more_items == true) but omits/zeroes the last_assetid cursor, so the next page request would be malformed or repeat the first page.","commonSituations":"Steam GC pagination bug under load; very large inventories where cursor handling breaks; partial response cut off mid-page.","solutions":["Retry the request; pagination cursor issues are usually transient.","Reduce itemsCountPerRequest so each page is smaller and more likely to include a valid cursor.","If persistent for a specific account, fall back to the ArchiWebHandler HTTP inventory path.","Report upstream — a non-zero more_items with zero cursor is a Steam-side contract violation."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Prefer smaller pages so a valid last_assetid cursor is returned.\nushort pageSize = Math.Min(itemsCountPerRequest, (ushort) 2000);","typeGuard":null,"tryCatchPattern":"try { await foreach (var a in handler.GetMyInventoryAsync(itemsCountPerRequest: 2000)) { /* ... */ } }\ncatch (NotSupportedException ex) when (ex.Message.Contains(\"last_assetid\")) {\n    // pagination broken; retry or fall back to HTTP path\n}","preventionTips":["Use smaller page sizes for very large inventories.","Treat cursor failures as transient; retry before escalating.","Fall back to ArchiWebHandler when GC pagination misbehaves."],"tags":["steam","inventory","pagination","data-integrity","gamecoordinator"],"backgroundTag":null,"analyzedSha":"fe57c4129f4ce02d452318cabc525eb49a087255","analyzedAt":"2026-08-13T17:26:20.146Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}