Description
Hi all, I used Claude to help me with this (hope that's alright). Happy to provide additional details if it helps!
Gitea Version: 1.25.4
Git Version: 2.53.0.windows.1
Operating System: Windows Server (Build 26200)
Can you reproduce the bug? Always
Description
When editing a file in the web editor and triggering the "Preview Changes" tab, Gitea returns a 500 Internal Server Error. Normal diff views on committed changes work fine. Only the live diff preview (_preview endpoint) is affected.
Steps to Reproduce
- Navigate to any file in a repository
- Click the edit (pencil) icon
- Make any change to the file content
- Click the Preview Changes tab
Expected Behavior
A diff of the changes is shown.
Actual Behavior
500 Internal Server Error:
GetDiffPreview, unable to run diff-index pipeline in temporary repo: exec: canceling Cmd: TerminateProcess: Access is denied.
Debug Logs
git.Run(by:files.(*TemporaryUploadRepository).DiffIndex, repo:.../local-repo\upload.git274962278): "C:\\Program Files\\Git\\bin\\git.exe" diff-index --src-prefix=\a/ --dst-prefix=\b/ --cached -p HEAD
services/gitdiff/gitdiff.go:487:ParsePatch() [D] ParsePatch(1000, 5000, 100, ..., )
.../repository/files/temp_repo.go:413:(*TemporaryUploadRepository).DiffIndex() [E] Unable to diff-index in temporary repo user/test (C:\gitea\data\tmp\local-repo\upload.git274962278). Error: exec: canceling Cmd: TerminateProcess: Access is denied.
Stderr:
.../repo/editor_preview.go:32:DiffPreviewPost() [E] GetDiffPreview: unable to run diff-index pipeline in temporary repo: exec: canceling Cmd: TerminateProcess: Access is denied.
Root Cause
In modules/repository/files/temp_repo.go, DiffIndex() runs git diff-index via a pipeline and reads stdout with ParsePatch(). After ParsePatch finishes reading, the pipeline cleanup cancels/kills the git subprocess. The code correctly tries to suppress this expected error:
if err != nil && !gitcmd.IsErrorCanceledOrKilled(err) {
return nil, fmt.Errorf("unable to run diff-index pipeline in temporary repo: %w", err)
}
On Linux, killing a process that has already exited returns ESRCH, which IsErrorCanceledOrKilled handles gracefully. On Windows, calling TerminateProcess on an already-exited process returns ERROR_ACCESS_DENIED instead. IsErrorCanceledOrKilled in modules/git/gitcmd/error.go currently only checks for context.Canceled and signal: killed:
func IsErrorCanceledOrKilled(err error) bool {
return errors.Is(err, context.Canceled) || IsErrorSignalKilled(err)
}
It does not recognise the Windows-specific TerminateProcess: Access is denied error string, so it returns false and the error propagates as a 500 — even though ParsePatch completed successfully and the diff data is available.
Suggested Fix
Add a Windows check to IsErrorCanceledOrKilled in modules/git/gitcmd/error.go. strings is already imported in that file:
func IsErrorCanceledOrKilled(err error) bool {
// When "cancel()" a git command's context, the returned error of "Run()" could be one of them:
// - context.Canceled
// - *exec.ExitError: "signal: killed"
// - On Windows: "TerminateProcess: Access is denied" (process already exited before kill)
return errors.Is(err, context.Canceled) || IsErrorSignalKilled(err) || strings.Contains(err.Error(), "TerminateProcess: Access is denied")
}
This single change fixes all call sites at once (batch.go, githttp.go, gitdiff.go, temp_repo.go) since they all rely on this function.
Configuration
- Running as a Windows Service via WinSW (LocalSystem account)
- Git binary:
C:\Program Files\Git\bin\git.exe
- Temp repo path:
C:\gitea\data\tmp\local-repo
- SQLite database
- Reverse proxy: Caddy
How are you running Gitea?
Via binary on Windows Server (Build 26200)
Database
SQLite
Description
Hi all, I used Claude to help me with this (hope that's alright). Happy to provide additional details if it helps!
Gitea Version: 1.25.4
Git Version: 2.53.0.windows.1
Operating System: Windows Server (Build 26200)
Can you reproduce the bug? Always
Description
When editing a file in the web editor and triggering the "Preview Changes" tab, Gitea returns a 500 Internal Server Error. Normal diff views on committed changes work fine. Only the live diff preview (
_previewendpoint) is affected.Steps to Reproduce
Expected Behavior
A diff of the changes is shown.
Actual Behavior
500 Internal Server Error:
Debug Logs
Root Cause
In
modules/repository/files/temp_repo.go,DiffIndex()runsgit diff-indexvia a pipeline and reads stdout withParsePatch(). AfterParsePatchfinishes reading, the pipeline cleanup cancels/kills the git subprocess. The code correctly tries to suppress this expected error:On Linux, killing a process that has already exited returns
ESRCH, whichIsErrorCanceledOrKilledhandles gracefully. On Windows, callingTerminateProcesson an already-exited process returnsERROR_ACCESS_DENIEDinstead.IsErrorCanceledOrKilledinmodules/git/gitcmd/error.gocurrently only checks forcontext.Canceledandsignal: killed:It does not recognise the Windows-specific
TerminateProcess: Access is deniederror string, so it returnsfalseand the error propagates as a 500 — even thoughParsePatchcompleted successfully and the diff data is available.Suggested Fix
Add a Windows check to
IsErrorCanceledOrKilledinmodules/git/gitcmd/error.go.stringsis already imported in that file:This single change fixes all call sites at once (
batch.go,githttp.go,gitdiff.go,temp_repo.go) since they all rely on this function.Configuration
C:\Program Files\Git\bin\git.exeC:\gitea\data\tmp\local-repoHow are you running Gitea?
Via binary on Windows Server (Build 26200)
Database
SQLite