#Requires -Version 5.1 <# JbTecWiz Support Centre -- generated fix script Fault : "File is locked for editing by another user" -- including when that user is you Fix : Close the stale handle on the file server Source: https://jbtecwiz.com/support/win-office-file-locked Run as : PowerShell as administrator on the file server Expect : 20 minutes Risk : medium Reversible : NO -- read the undo note below before running WHEN THIS IS THE RIGHT FIX The owner file is gone or absent and the lock persists, and the file is on a Windows file server. Closing a handle that is genuinely in use discards any unsaved work in that session without warning the person doing the work, so confirm the session is stale before you close anything. HOW TO UNDO IT A closed handle cannot be reopened server-side -- the client simply reconnects. This script walks the fix one step at a time and asks before each one. Steps with no command are things you do yourself -- it prints those and waits. Run with -DryRun to print without executing. #> [CmdletBinding()] param( # from the write-up. PowerShell prompts for this # by name if you do not pass it on the command line. [Parameter(Mandatory = $true)][string]$SessionId, # from the write-up. PowerShell prompts for this # by name if you do not pass it on the command line. [Parameter(Mandatory = $true)][string]$FileId, # Print every step and command without running anything. [switch]$DryRun, # Do not ask before each step. Read the script first if you use this. [switch]$Unattended ) $ErrorActionPreference = 'Stop' $script:Failed = 0 # This fix is documented as needing an elevated session. $id = [Security.Principal.WindowsIdentity]::GetCurrent() if (-not (New-Object Security.Principal.WindowsPrincipal $id).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host '' Write-Host ' This fix needs an elevated PowerShell.' -ForegroundColor Yellow Write-Host ' Close this window, right-click PowerShell, Run as administrator,' Write-Host ' and run the script again.' Write-Host '' exit 3 } function Write-Rule { param([string]$Text) Write-Host '' Write-Host ('-' * 70) -ForegroundColor DarkGray if ($Text) { Write-Host $Text -ForegroundColor Cyan } } function Show-Prose { param([string]$Text, [string]$Colour = "Gray") if (-not $Text) { return } $words = $Text -split "\s+"; $line = " " foreach ($w in $words) { if (($line.Length + $w.Length) -gt 74) { Write-Host $line -ForegroundColor $Colour; $line = " " } $line += "$w " } if ($line.Trim()) { Write-Host $line -ForegroundColor $Colour } } function Invoke-Step { param( [int]$Number, [string]$Do, [string]$Why, [scriptblock]$Command, [switch]$Manual, [string]$Shell = "powershell" ) Write-Rule " Step $Number of 5" Show-Prose $Do "White" if ($Why) { Write-Host ""; Show-Prose $Why "DarkGray" } if ($Manual) { Write-Host '' Write-Host ' -> Do this yourself, then press Enter to carry on.' -ForegroundColor Yellow if (-not $Unattended -and -not $DryRun) { [void](Read-Host) } return } Write-Host '' foreach ($l in ($Command.ToString().Trim() -split "`n")) { Write-Host (" " + $l.Trim()) -ForegroundColor Green } Write-Host '' if ($DryRun) { Write-Host " (dry run -- not executed)" -ForegroundColor DarkGray; return } if (-not $Unattended) { $a = Read-Host " Run this step? [Y]es / [S]kip / [Q]uit" if ($a -match "^[Qq]") { Write-Host " Stopped at your request."; exit 0 } if ($a -match "^[Ss]") { Write-Host " Skipped." -ForegroundColor DarkGray; return } } try { & $Command } catch { $script:Failed++ Write-Host (" Step $Number failed: " + $_.Exception.Message) -ForegroundColor Red Show-Prose "The rest of the fix may depend on this. Read the write-up before carrying on." "Red" if (-not $Unattended) { $c = Read-Host " Carry on anyway? [y/N]" if ($c -notmatch "^[Yy]") { exit 1 } } } } Write-Rule Write-Host ' "File is locked for editing by another user" -- including when that user is you' -ForegroundColor White Write-Host ' Close the stale handle on the file server' -ForegroundColor Cyan Write-Host '' Write-Host ' Risk: medium NOT REVERSIBLE 20 minutes' -ForegroundColor Yellow Write-Host '' Show-Prose 'Undo: A closed handle cannot be reopened server-side -- the client simply reconnects.' 'Yellow' Write-Rule if (-not $Unattended -and -not $DryRun) { $go = Read-Host ' Ready? [y/N]' if ($go -notmatch "^[Yy]") { Write-Host " Nothing was changed."; exit 0 } } Invoke-Step -Number 1 -Do 'Find the open handle by file name.' -Command { Get-SmbOpenFile | Where-Object Path -like '*Budget.xlsx*' | Select-Object FileId, SessionId, ClientUserName, ClientComputerName, Path } Invoke-Step -Number 2 -Do 'Check whether that session is actually alive before closing anything.' -Why 'A high SecondsIdle on a session whose owner says they are not in the file is the confirmation you want. Closing a live handle discards whatever that client has not yet written.' -Command { Get-SmbSession -SessionId $SessionId | Select-Object ClientComputerName, ClientUserName, NumOpens, SecondsIdle } Invoke-Step -Number 3 -Do 'Close the handle.' -Command { Close-SmbOpenFile -FileId $FileId -Force } Invoke-Step -Number 4 -Do 'Have the user reopen the document.' -Manual Invoke-Step -Number 5 -Do 'If this happens repeatedly on the same share, look at the underlying cause rather than closing handles forever -- an unstable VPN, roaming laptops sleeping with files open, or oplocks misbehaving with a third-party filter driver are the usual three.' -Manual Write-Rule " Confirm it worked" Show-Prose 'The handle no longer appears and the document opens for editing.' 'White' Write-Host '' if (-not $DryRun) { Get-SmbOpenFile | Where-Object Path -like '*Budget.xlsx*' } Write-Rule if ($script:Failed -gt 0) { Write-Host (" Finished with " + $script:Failed + " failed step(s).") -ForegroundColor Yellow Show-Prose 'Read the full write-up at https://jbtecwiz.com/support/win-office-file-locked' 'Yellow' } else { Write-Host ' Finished.' -ForegroundColor Green } Write-Host '' Show-Prose 'To undo: A closed handle cannot be reopened server-side -- the client simply reconnects.' 'DarkGray' Write-Rule