iOfficeAI/OfficeCLI · critical · InvalidOperationException

Corrupt file: worksheet data missing

Error message

Corrupt file: worksheet data missing

What it means

Thrown by GetSheet when a WorksheetPart's .Worksheet property is null. The part exists in the package but its underlying sheet XML is missing or unreadable — a corrupt OOXML package, not a caller bug. InvalidOperationException (not ArgumentException) signals file-level corruption.

Source

Thrown at src/officecli/Handlers/Excel/ExcelHandler.Helpers.Sheet.cs:189

    /// Resolve sheet[N] index references in the first segment of a normalized path.
    /// E.g. /sheet[1]/A1 → /Sheet1/A1 (if the first sheet is named "Sheet1").
    /// Must be called after NormalizeExcelPath.
    /// </summary>
    private string ResolveSheetIndexInPath(string path)
    {
        if (!path.StartsWith('/')) return path;
        var trimmed = path[1..]; // remove leading '/'
        var slashIdx = trimmed.IndexOf('/');
        var firstSegment = slashIdx >= 0 ? trimmed[..slashIdx] : trimmed;
        var resolved = ResolveSheetName(firstSegment);
        if (resolved == firstSegment) return path;
        return slashIdx >= 0 ? $"/{resolved}/{trimmed[(slashIdx + 1)..]}" : $"/{resolved}";
    }

    // ==================== Private Helpers ====================

    private static Worksheet GetSheet(WorksheetPart part) =>
        part.Worksheet ?? throw new InvalidOperationException("Corrupt file: worksheet data missing");

    /// <summary>
    /// Mark a worksheet as dirty. The actual save (with schema-order reorder) is
    /// deferred to <see cref="FlushDirtyParts"/> which runs in Dispose().
    /// This replaces per-mutation Save() calls — batch operations over many cells
    /// previously triggered one disk write per cell (O(n) saves); now they all
    /// flush in a single pass at the end.
    /// </summary>
    private void SaveWorksheet(WorksheetPart part)
    {
        _dirtyWorksheets.Add(part);
    }

    /// <summary>
    /// Flush all pending worksheet and stylesheet saves. Called from Dispose().
    /// Each dirty WorksheetPart is reordered and saved exactly once regardless
    /// of how many mutations targeted it.
    /// </summary>

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Open the file in Excel and let it repair, then re-save.
  2. Regenerate the file from the source of truth.
  3. Inspect xl/worksheets/_rels/*.rels and the target sheetN.xml to find the broken relationship.

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

// Cannot meaningfully pre-validate; treat the document as untrusted at load time
static bool TryGetWorksheet(WorksheetPart part, out Worksheet ws)
{
    ws = part?.Worksheet;
    return ws != null;
}

Type guard

null

Try / catch

try { /* any cell/range/table op */ }
catch (InvalidOperationException ex) when (ex.Message == "Corrupt file: worksheet data missing")
{ // mark file as corrupt, do not retry on same file; offer repair/regenerate path }

Prevention

When it happens

Trigger: Opening any .xlsx where xl/worksheets/sheetN.xml is empty, malformed, or removed but the relationship still points at it. Reading any cell/range/table on that sheet triggers GetSheet.

Common situations: Files from a buggy generator that wrote the rels but not the sheet XML; zip-level corruption after a partial write; manual package edits that deleted a sheet part.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/2e2a0d39614aa023. Report an issue: GitHub.