iOfficeAI/OfficeCLI · error · IOException

Cannot read OLE source file '{srcPath}': the file is locked

Error message

Cannot read OLE source file '{srcPath}': the file is locked by another process. If an officecli resident or watch process has this file open, run 'officecli close {srcPath}' first, then retry.

What it means

Thrown by OleHelper.AddEmbeddedPart when File.ReadAllBytes(srcPath) throws an IOException, which is re-thrown with an actionable hint. The file is locked by another process that holds an exclusive or incompatible lock. The most common cause within OfficeCLI is that a resident or watch process has the source file open. The message suggests running 'officecli close <path>' to release the lock. The code also cleans up the dangling embedded part that was already created before the read failed, so no orphan part is left on the host.

Source

Thrown at src/officecli/Core/OleHelper.cs:256

        // message is useful even for non-officecli holders.
        //
        // CONSISTENCY(ole-orphan-cleanup): if FileStream.Open() or FeedData()
        // fails after the host part has been created, delete the dangling
        // part so we don't leave an orphan EmbeddedPackagePart/EmbeddedObjectPart
        // on the host (which would inflate part counts and survive into
        // the saved file). The part was just added by AddEmbeddedPackagePart/
        // AddEmbeddedObjectPart above — at this point nothing else references
        // it, so DeletePart is safe.
        try
        {
            byte[] srcBytes;
            try
            {
                srcBytes = File.ReadAllBytes(srcPath);
            }
            catch (IOException ioEx)
            {
                throw new IOException(
                    $"Cannot read OLE source file '{srcPath}': the file is locked by another process. " +
                    $"If an officecli resident or watch process has this file open, run " +
                    $"'officecli close {srcPath}' first, then retry.", ioEx);
            }

            // CONSISTENCY(ole-cfb-wrap): non-Office payloads (.pdf/.txt/binary)
            // must be wrapped in a CFB container with a \x01Ole10Native stream.
            // Excel rejects the file (0x800A03EC) otherwise. Office OOXML
            // payloads are embedded raw via EmbeddedPackagePart — Excel reads
            // them directly using the progId (Word.Document.12 / etc).
            byte[] payload = kind == EmbeddingKind.Object
                ? BuildOle10NativeCfb(srcBytes, Path.GetFileName(srcPath))
                : srcBytes;

            using var payloadStream = new MemoryStream(payload);
            part.FeedData(payloadStream);
        }
        catch

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Run 'officecli close <srcPath>' to release the lock held by a resident or watch process.
  2. Close the external application (Excel, Word, etc.) that has the file open.
  3. Wait a moment and retry if the lock is transient (antivirus scan, backup).
  4. Copy the source file to a temporary path and embed the copy instead, avoiding the lock contention.

Example fix

// before — source file is locked
add ole src='/home/user/report.xlsx' path='/body'

// after — release the lock first
// officecli close /home/user/report.xlsx
// then retry
add ole src='/home/user/report.xlsx' path='/body'
// or copy to a temp file to avoid contention
cp /home/user/report.xlsx /tmp/report_copy.xlsx
add ole src='/tmp/report_copy.xlsx' path='/body'
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check: verify the file is readable (not locked) before embedding
try
{
    using var testStream = File.Open(srcPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
catch (IOException)
{
    Console.Error.WriteLine($"File is locked: {srcPath}. Run 'officecli close {srcPath}' first.");
    return;
}

Try / catch

try
{
    var (relId, part) = OleHelper.AddEmbeddedPart(hostPart, srcPath, hostDocumentPath);
}
catch (IOException ex) when (ex.Message.Contains("locked by another process"))
{
    // Release the lock (officecli close) or copy the file, then retry
    Console.Error.WriteLine(ex.Message);
}

Prevention

When it happens

Trigger: Calling Add ole with a srcPath that is currently open by another process with an incompatible FileShare mode. This happens when: an officecli resident/watch process has the file open, the host OOXML SDK holds an exclusive package lock on the file (though self-embed is handled separately), or an external application (Excel, an editor, an antivirus scanner) has the file locked. The original IOException is preserved as the InnerException.

Common situations: An officecli resident server that was started earlier and holds the source file open. A concurrent batch process that embeds the same file simultaneously. An external Office application that opened the file for editing. An antivirus or backup tool that briefly locks the file. The host document itself is the source (self-embed), but this is handled by the zero-byte placeholder path before reaching File.ReadAllBytes.

Related errors


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