Skip to content

[Bug] Diff preview (editor _preview) fails on Windows with "TerminateProcess: Access is denied" #36698

@wbste

Description

@wbste

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

  1. Navigate to any file in a repository
  2. Click the edit (pencil) icon
  3. Make any change to the file content
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    issue/needs-feedbackFor bugs, we need more details. For features, the feature must be described in more detailtype/bug

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions