{"record":{"id":"7f9cb79bc0db0a93","repo":"microsoft/garnet","slug":"no-snapshot-object-log-data-available-while-copyin","errorCode":null,"errorMessage":"No snapshot object-log data available while copying objects during recovery","messagePattern":"No snapshot object-log data available while copying objects during recovery","errorType":"exception","errorClass":"TsavoriteException","httpStatus":null,"severity":"critical","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/ObjectAllocatorImpl.cs","lineNumber":969,"sourceCode":"                                        // Snapshot-region recovery flush: the record's objects live only in the snapshot object-log. Copy their bytes\n                                        // into the main object-log (appended at the current objectLogTail via logWriter) so the page becomes durable and\n                                        // can be evicted, then repoint the disk-image record to that main object-log position. The objects are NOT\n                                        // deserialized at this point, so read the position/lengths from the R11 encoding (not from objectIdMap), and use\n                                        // RepointObjectLogPosition (which preserves the unchanged lengths) rather than SetRecoveredObjectLogRecordStartPosition.\n                                        var snapshotPositionWord = logRecord.GetObjectLogRecordStartPositionAndLengths(out var copyKeyLength, out var copyValueLength);\n                                        var copyObjectLength = (ulong)copyKeyLength + copyValueLength;\n\n                                        // Demand-load the snapshot object reader on the first valid record with objects, so pages with few or no object\n                                        // records avoid an up-front full-page pre-pass. The read-ahead range is sized by scanning forward from here.\n                                        snapshotObjectReader ??= CreateSnapshotObjectReader(physicalAddress + logRecordSize, endPhysicalAddress, snapshotPositionWord,\n                                            copyKeyLength, copyValueLength, asyncResult.recoverySnapshotObjectLogDevice, out snapshotObjectReadBuffers);\n\n                                        var mainRecordPosition = logWriter.GetNextRecordStartPosition();\n\n                                        // Position/await the snapshot read buffers at this record (skips sector padding and waits for the read-ahead IO),\n                                        // then stream the record's bytes verbatim into the main object-log.\n                                        if (!snapshotObjectReadBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(snapshotPositionWord, objectLogTail.SegmentSizeBits)))\n                                            throw new TsavoriteException(\"No snapshot object-log data available while copying objects during recovery\");\n                                        logWriter.CopyRecoveredObjectBytes(snapshotObjectReader, copyObjectLength);\n                                        logRecord.RepointObjectLogPosition(mainRecordPosition);\n                                        recoveryOngoingPageHeader.Advance(copyObjectLength);\n                                    }\n                                    else\n                                    {\n                                        // In recovery we just need to update the disk-image LogRecord with the object lengths and file position, and then\n                                        // advance the recoveryOngoingPageHeader position. This advancement will also take care of segment breaks if needed.\n                                        var objectLengths = logRecord.SetRecoveredObjectLogRecordStartPosition(recoveryOngoingPageHeader);\n                                        recoveryOngoingPageHeader.Advance(objectLengths);\n                                    }\n                                }\n\n                                // Do this for both cases so it's clear when debugging\n                                isFirstRecordOnPage = false;\n                            }\n                        }\n                        else","sourceCodeStart":951,"sourceCodeEnd":987,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/ObjectAllocatorImpl.cs#L951-L987","documentation":"Thrown during object-log recovery when, for a main-log record that carries objects, the snapshot object-log read buffers report no data at the record's object-log position (snapshotObjectReadBuffers.OnBeginRecord returns false). This means the snapshot object-log does not contain the bytes the main log record references — the snapshot and main log are inconsistent or the snapshot object-log is truncated.","triggerScenarios":"Recovery with object store where the snapshot object-log device (recoverySnapshotObjectLogDevice) is missing, shorter than, or out of sync with the main log snapshot. Triggered on the first valid record whose objects must be copied when the demand-loaded snapshot object reader cannot position at the record.","commonSituations":"Checkpoint object-log file deleted or partially restored while the main-log checkpoint is present; snapshot of main log and object log taken from different checkpoints; object-log device path differs from the one used at checkpoint time; corruption of the object-log snapshot.","solutions":["Restore the complete checkpoint, including the object-log snapshot device, from the same checkpoint.","Ensure the ObjectLogDevice path used at recovery matches the one used when the checkpoint was written.","If the object-log snapshot is unusable, recover from an earlier consistent checkpoint or discard and restart.","Verify checkpoint integrity (both main and object log files present and sized correctly) before Recover."],"exampleFix":"// before: only main-log device restored\nstore.Recover();\n// after: restore the matching object-log snapshot device too\nlogSettings.ObjectLogDevice = Devices.CreateLogDevice(objectLogPath);\nstore.Recover();","handlingStrategy":"validation","validationCode":"// Ensure both devices exist and the object-log snapshot is non-empty before Recover\nif (!File.Exists(mainLogPath)) throw new FileNotFoundException(mainLogPath);\nif (logSettings.ObjectLogDevice is null) throw new InvalidOperationException(\"ObjectLogDevice required for object-store recovery\");","typeGuard":null,"tryCatchPattern":"try { store.Recover(); }\ncatch (TsavoriteException ex) when (ex.Message.Contains(\"No snapshot object-log data available\"))\n{\n    logger.LogError(ex, \"Object-log snapshot missing/inconsistent with main-log snapshot\");\n    // restore matching checkpoint (main + object log) and retry\n    throw;\n}","preventionTips":["Always checkpoint main and object logs together and restore them together.","Confirm the ObjectLogDevice path matches the one used at checkpoint time.","Validate snapshot file sizes against the highest referenced position before recovery."],"tags":["recovery","object-log","checkpoint","snapshot"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}