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);
}
catchView on GitHub (pinned to 1ced45e900)
Solutions
- Run 'officecli close <srcPath>' to release the lock held by a resident or watch process.
- Close the external application (Excel, Word, etc.) that has the file open.
- Wait a moment and retry if the lock is transient (antivirus scan, backup).
- 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
- Run 'officecli close <path>' before embedding files that may be held open by a resident/watch process.
- Close external applications (Excel, Word) that have the source file open before embedding it.
- Copy the source file to a temp location to avoid lock contention with resident processes.
- For self-embed scenarios (embedding the host document into itself), the code handles it with a zero-byte placeholder — no manual intervention needed.
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
- file_locked
- OLE source file not found: {srcPath}
- Cannot embed a workbook into itself: the source file is the
- -1
- ${r.stderr || r.stdout}
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/4988422b9ce2188a.
Report an issue: GitHub.