Skip to content

Report an actionable error when the PrjFlt driver service is disabled - #2122

Open
Tyrie Vella (tyrielv) wants to merge 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/fix-prjflt-disabled-start
Open

Tyrie Vella (tyrielv) wants to merge 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/fix-prjflt-disabled-start

Conversation

@tyrielv

Copy link
Copy Markdown
Contributor

Problem and Context

VFS for Git requires the PrjFlt ProjFS minifilter driver on every volume. The driver's load behavior is governed by its service Start type. A security-hardening baseline, an anti-virus/EDR product, Group Policy, or manual troubleshooting can set the service to Disabled. When that happens, VFS for Git cannot start the driver.

Today ProjFSFilter.TryStartService calls ServiceController.Start() on the disabled service. The call fails with ERROR_SERVICE_DISABLED (1058), which is caught by a generic Win32Exception handler. EnableAndAttachProjFSHandler.TryEnablePrjFlt then reports the dead-end message "Failed to start prjflt service" — with no indication that the driver is disabled and no hint at the fix. No other code path detects or reports this state. (The existing AutoLogger Start handling is an unrelated ETW logging key, not the driver service.)

This change detects the disabled state and reports it with an actionable message. It does not re-enable the driver: a disabled driver is often disabled deliberately by an administrator or a security tool, so silently re-enabling it would change the machine's security posture without consent.

Changes

  • ProjFSFilter.TryStartService now checks ServiceController.StartType. When the service is Disabled, it returns a specific error that names the driver and gives the exact elevated command to re-enable it (sc.exe config prjflt start= auto). It also maps ERROR_SERVICE_DISABLED from a failed Start() as a fallback for the disable-after-check race.
  • TryStartService now reports its failure through an out string error parameter instead of returning only a bool, so the caller can surface the specific message.
  • EnableAndAttachProjFSHandler.TryEnablePrjFlt propagates that message instead of the generic "Failed to start prjflt service", and records the start-failure reason in the TryEnablePrjFlt_Summary health telemetry so a disabled driver is detectable in aggregate.
  • Added a pure internal GetStartServiceFailureError(int nativeErrorCode) seam and unit tests in ProjFSFilterTests covering the disabled error code and other start-failure codes.

@tyrielv
Tyrie Vella (tyrielv) marked this pull request as ready for review October 6, 2026 16:20

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review pass over this PR. 5 comment(s).

Comment thread GVFS/GVFS.Platform.Windows/ProjFSFilter.cs Outdated
Comment thread GVFS/GVFS.UnitTests/Windows/Platform/ProjFSFilterTests.cs Outdated
Comment thread GVFS/GVFS.Platform.Windows/ProjFSFilter.cs Outdated
Comment thread GVFS/GVFS.Platform.Windows/ProjFSFilter.cs Outdated
Comment thread GVFS/GVFS.Platform.Windows/ProjFSFilter.cs Outdated
@tyrielv
Tyrie Vella (tyrielv) force-pushed the tyrielv/fix-prjflt-disabled-start branch from d0efb6b to 3187f0f Compare October 13, 2026 20:03

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review of the amended commit. All five of my earlier threads are genuinely addressed — thank you. Two new findings below; the first one I think inverts the PR's premise, so flagging it before this lands.

CI is green across every leg.

Comment thread GVFS/GVFS.Platform.Windows/ProjFSFilter.cs Outdated
Comment thread GVFS/GVFS.Service/Handlers/EnableAndAttachProjFSHandler.cs Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current head is unchanged since the prior review. Requesting changes for the existing High finding: wrapped native service-start failures still enter the InvalidOperationException handler and are reported as "service was not found," bypassing the intended Win32 error mapping. Please address the existing thread and cover the wrapped-exception shape before merge:
#2122 (comment)

The existing Medium request for stable telemetry reason codes also remains:
#2122 (comment)

These are references to the original findings, not new duplicate inline threads.

VFS for Git requires the PrjFlt ProjFS minifilter driver on every volume.
Its load behavior is governed by its service Start type. When a security
baseline, an anti-virus product, Group Policy, or manual troubleshooting
disables the service, the driver cannot start.

Today ProjFSFilter.TryStartService calls ServiceController.Start() on the
disabled service. That fails with ERROR_SERVICE_DISABLED (1058), which is
caught by a generic Win32Exception handler and surfaces to the user as the
dead-end message "Failed to start prjflt service" with no indication that
the driver is disabled and no way to fix it. Nothing else in the product
detects this state (the existing AutoLogger Start handling is an unrelated
ETW logging key).

Detect the disabled state and report it, rather than silently re-enabling a
driver that an administrator or security tool may have disabled on purpose.

Changes:
- TryStartService checks the service start type only when the service is not
  already running, so an already-running service succeeds regardless of its
  start type. When the service is not running and its start type is Disabled,
  it returns a specific error naming the driver and the exact elevated command
  to re-enable it.
- ServiceController reports a failed native service call as an
  InvalidOperationException that wraps the Win32Exception. TryStartService
  unwraps it and maps the native error code to a ServiceStartFailureReason:
  ERROR_SERVICE_DISABLED is ServiceDisabled, ERROR_SERVICE_DOES_NOT_EXIST is
  ServiceNotFound, and any other code is StartFailed. Only a missing service
  is reported as "not found".
- TryStartService reports its result through out parameters, so the caller
  surfaces the specific message instead of the generic "Failed to start prjflt
  service".
- EnableAndAttachProjFSHandler.TryEnablePrjFlt records the
  ServiceStartFailureReason in the TryEnablePrjFlt_Summary health telemetry.
  The reason is a stable code, so rewording the user-facing message does not
  change the metric.
- Added unit tests that cover the disabled and enabled start types, and the
  error-code mapping through the wrapped InvalidOperationException shape.

Assisted-by: Claude Opus 4.8
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
@tyrielv
Tyrie Vella (tyrielv) force-pushed the tyrielv/fix-prjflt-disabled-start branch from 3187f0f to c248802 Compare October 28, 2026 22:35
@KeithIsSleeping

Copy link
Copy Markdown

[KeithBot]: 🟡 Medium · Design — Installer drops this message: exit code 4 still shows a dead-end fatal error

Location: /GVFS/GVFS.Installers/Setup.iss:563

After install or upgrade, Setup.iss runs gvfs service --mount-all; with PrjFlt disabled it exits 4 and prints this new remedy. The exit-4 branch raises a fixed 'Fatal: Could not configure and start Windows Projected File System.' and never shows the captured MountOutput, so installs and reinstalls on such machines still end in the dead-end error.

Evidence:
gvfs service --mount-all (built at c248802), stdout redirected like ExecWithResult, against a stub service pipe returning the handler's disabled-driver error:
exit code: 4
stdout: Failed to enable PrjFlt: The PrjFlt driver service is disabled and cannot be started. [...] sc.exe config prjflt start= auto
Setup.iss:563-565 then runs RaiseException('Fatal: Could not configure and start Windows Projected File System.'); MountOutput is only shown by the non-4 branch (567-570).

Fix: Include the captured output in the FilterError branch, e.g. RaiseException('Fatal: Could not configure and start Windows Projected File System.' + #13#10 + MountOutput);. Optionally also Log(MountOutput) so silent installs keep the remedy in the setup log.

🤖 KeithBot · 1/3 models · verified locally

@KeithIsSleeping

Copy link
Copy Markdown

[KeithBot]: ⚠️ 1 issue · iteration 1 (c248802)

  1. 🟡 Medium Installer drops this message: exit code 4 still shows a dead-end fatal error

Validated: ✅ Build GVFS.UnitTests at source + base · ✅ ProjFSFilterTests 17/17 at source · ⚠️ Full unit suite 984/996 at source · ⚠️ Mutation of fix parts vs new tests · ✅ Real-SCM harness, read-only · ⚠️ Mocked-SCM error matrix 25/25 · ✅ gvfs service --mount-all via stub pipe · ✅ NativeAOT keeps enum names · ⏭️ Installer (Inno Setup) exit-4 path · ⏭️ Functional tests (ServiceTests)

🤖 KeithBot · claude-opus-5.5, gpt-6.1-sol, gemini-4-argon · reply on a thread to dispute

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants