# How to Remove a Dirty Worktree in Pi Web: API Behavior and Force Deletion

> Learn how to remove a dirty worktree in Pi Web using the API. Discover the 409 Conflict response and how to force deletion with the force flag to bypass uncommitted changes.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: how-to-guide
- Published: 2026-07-30

---

**When you attempt to remove a dirty worktree through Pi Web's DELETE API without the `force` flag, the server returns HTTP 409 Conflict containing `{ "error": "...", "dirty": true }` to signal that uncommitted changes exist, while including `force: true` bypasses this protection by appending `--force` to the underlying Git command.**

The `agegr/pi-web` repository provides a Next.js-based interface for managing Git worktrees via REST endpoints. Understanding how the application handles dirty worktree removal is critical for building robust UI flows that prevent accidental data loss while offering force-delete capabilities when users explicitly confirm destructive actions.

## The DELETE Request Flow in `/api/worktrees`

When a client sends a DELETE request to `/api/worktrees`, the route handler in [`app/api/worktrees/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/worktrees/route.ts) executes a validation and removal process that delegates to Git's native worktree commands.

### Input Validation and Access Control

First, the endpoint validates that both `cwd` and `path` parameters are present in the request body, then verifies the caller has permission to access the target directory (lines 78‑85). If validation fails, the request rejects before any Git operations execute, preventing unauthorized filesystem modifications.

### Git Worktree Removal Execution

The handler invokes `removeWorktree()` from [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts), passing the `cwd`, `path`, and an optional `force` boolean (lines 88‑89). This function executes the Git command:

```typescript
await git(cwd, ["worktree", "remove", ...(force ? ["--force"] : []), worktreePath]);

```

Without the `--force` flag, Git refuses to delete worktrees containing modified or untracked files, throwing an error that propagates through the helper `extractGitError()` (lines 18‑22) and returns to the API route handler.

## Dirty Worktree Detection and Error Handling

The route's catch block specifically differentiates dirty worktree conditions from other Git failures using regex pattern matching.

### The 409 Conflict Response

When Git rejects the deletion, the handler tests the error message against:

```typescript
const dirty = /contains modified or untracked files|is dirty/i.test(message);

```

If matched, Pi Web returns HTTP **409 Conflict** with the JSON payload:

```json
{ "error": "<git-error-message>", "dirty": true }

```

For non-dirty errors—such as invalid paths or permission issues—the API returns **400 Bad Request** without the `dirty` flag. This distinction enables frontend applications to render specific confirmation dialogs only when uncommitted changes actually exist, rather than for generic failures.

## Practical Implementation Examples

### Safe Removal of a Clean Worktree

```typescript
await fetch('/api/worktrees', {
  method: 'DELETE',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ 
    cwd: '/home/me/project', 
    path: '/home/me/project-worktrees/feature-x' 
  })
});
// Returns: { success: true } when worktree is clean

```

### Handling Dirty Worktree Errors

```typescript
const response = await fetch('/api/worktrees', {
  method: 'DELETE',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ 
    cwd: '/home/me/project', 
    path: '/home/me/project-worktrees/feature-y' 
  })
});
const data = await response.json();

if (response.status === 409 && data.dirty) {
  // UI should prompt: "Worktree has local changes. Force delete?"
  console.warn('Dirty worktree detected:', data.error);
}

```

### Force Deleting After User Confirmation

```typescript
await fetch('/api/worktrees', {
  method: 'DELETE',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    cwd: '/home/me/project',
    path: '/home/me/project-worktrees/feature-y',
    force: true  // Appends --force to git worktree remove
  })
});
// Returns: { success: true } (destructive: discards uncommitted changes)

```

## Summary

- Pi Web's `/api/worktrees` endpoint validates all delete requests in [`app/api/worktrees/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/worktrees/route.ts) before invoking Git operations
- The `removeWorktree()` function in [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) executes `git worktree remove` with an optional `--force` flag depending on request parameters
- **Dirty worktrees trigger HTTP 409 Conflict** with `{ dirty: true }`, while other errors return **400 Bad Request**
- The regex `/contains modified or untracked files|is dirty/i` identifies dirty states from Git's error output (lines 92‑95)
- Including `force: true` in the request body bypasses the dirty check by appending `--force` to the Git command, requiring explicit user confirmation in the UI

## Frequently Asked Questions

### What HTTP status code does Pi Web return for a dirty worktree?

Pi Web returns **409 Conflict** when attempting to remove a dirty worktree without the force flag. This specific status code differentiates dirty states from other client errors (400 Bad Request), allowing frontend applications to handle cleanup confirmations distinctly from validation or permission failures.

### How do I force delete a worktree that has uncommitted changes?

Include `"force": true` in the DELETE request body. This passes the `--force` flag to `git worktree remove` via the `removeWorktree()` function, which deletes the worktree regardless of modified or untracked files. Always confirm this destructive action with the user in the UI before sending the forced request to prevent accidental data loss.

### Where does Pi Web detect if a worktree is dirty?

The detection occurs in [`app/api/worktrees/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/worktrees/route.ts) (lines 92‑95) within the error catch block. The code examines the Git error message using a case-insensitive regex looking for "contains modified or untracked files" or "is dirty", then sets the `dirty` boolean in the JSON response accordingly.

### What Git command does Pi Web execute to remove worktrees?

According to [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) (lines 19‑20), Pi Web executes `git worktree remove [path]` for normal deletions, or `git worktree remove --force [path]` when the force parameter is true. This executes inside the `removeWorktree()` function after the API route handler validates that the requester has access to the target directory.