# Troubleshooting

> Common failure modes: uninitialized workspaces, missing markers, CoW unavailable, unsafe Git state, hook failures, and platform limitations.

- Repository: anomalyco/rift
- GitHub: https://github.com/anomalyco/rift
- Human docs: https://grok-wiki.com/public/docs/anomalyco-rift-ea3fd5dbf662
- Complete Markdown: https://grok-wiki.com/public/docs/anomalyco-rift-ea3fd5dbf662/llms-full.txt

## Source Files

- `crates/cli/src/main.rs`
- `crates/core/src/lib.rs`
- `npm/rift-snapshot/index.d.ts`
- `README.md`
- `specs.md`
- `crates/core/src/git.rs`

---

---
title: "Troubleshooting"
description: "Common failure modes: uninitialized workspaces, missing markers, CoW unavailable, unsafe Git state, hook failures, and platform limitations."
---

Rift surfaces failures through stderr messages on the CLI (exit code `1`), `RiftError` objects from the JavaScript FFI bindings (`code`, `message`, optional `path`), and the `RiftError` enum in the Rust core. Most errors print the core message verbatim; three workspace-resolution errors get tailored CLI guidance. Use the symptom tables below to map output to cause and remediation.

## How failures are reported

| Surface | Success signal | Failure signal |
| --- | --- | --- |
| CLI | Exit code `0`; paths on stdout for `create`, `list`, `ancestors`, `gc` | Exit code `1`; message on stderr |
| JavaScript (`rift-snapshot`) | Return value (`string`, `string[]`, or `null`) | Thrown `RiftError` with `code` and optional `path` |
| FFI protocol | `{ "status": "ok", "value": ... }` | `{ "status": "error", "error": { "code", "message", "path?" } }` |

The CLI remaps three workspace errors to actionable guidance:

| Core error | CLI stderr message |
| --- | --- |
| `WorkspaceNotInitialized` | `no initialized workspace found; run rift init from the root folder` |
| `MissingMarker` | `this workspace is missing its .rift marker; run rift init to restore it` |
| `InitializationRequired` | `this workspace must be initialized first; run rift init from its root folder` |

All other errors use the core `Display` string (for example `copy-on-write cloning unavailable: ...` or `unsafe Git source: ...`).

<Note>
The JavaScript `init()` function initializes exactly the `at` path. Git-root selection and `--here` are CLI-only behaviors. If `init()` fails from a nested directory, pass the workspace root explicitly.
</Note>

## Workspace resolution failures

Rift locates managed workspaces by walking upward from the requested path. It reads `.rift` marker files and cross-checks them against the central SQLite registry.

```text
requested path
    │
    ▼ walk ancestors
┌─────────────────────────────────────┐
│ .rift marker present?               │
│   yes → verify ID in registry       │
│   no  → registry row at path?       │
│         yes → MissingMarker         │
│         no  → continue upward       │
└─────────────────────────────────────┘
    │
    ▼ no match found
WorkspaceNotInitialized
```

### No initialized workspace (`workspace_not_initialized`)

**Symptom:** `rift create`, `rift list`, `rift ancestors`, or `rift remove` from a directory with no `.rift` marker anywhere above it.

**Cause:** The path is not under a registered Rift workspace.

**Fix:**

```bash
cd /path/to/project-root
rift init
```

The CLI `rift init` (without `--here`) selects the nearest existing managed ancestor or the nearest Git root before calling core `init`. Use `rift init --here` to initialize exactly the current directory.

### Missing `.rift` marker (`missing_marker`)

**Symptom:** Registry has an active row for a directory, but `.rift` was deleted or never written.

**Cause:** Manual deletion of `.rift`, interrupted `init`, or filesystem damage.

**Fix:**

```bash
rift init
```

If the directory is already registered, `rift init` restores the marker from the existing registry identity without re-converting the filesystem.

### Workspace requires initialization (`initialization_required`)

**Symptom:** On Linux btrfs, `rift create` fails even though a `.rift` marker exists.

**Cause:** The source is an ordinary btrfs directory, not a subvolume. Btrfs snapshots require `rift init` to convert the workspace first.

**Fix:**

```bash
rift init    # converts ordinary btrfs directory → subvolume on first run
rift create
```

### Unknown or mismatched markers

| Error code | Meaning | Typical cause |
| --- | --- | --- |
| `unknown_marker` | `.rift` contains an ID not in the registry | Corrupt marker, wrong database, manual edit |
| `marker_mismatch` | Marker ID exists but path does not match registry | Marker copied to wrong directory, registry/path drift |

**Fix:** Restore the correct ULID from the registry into `.rift`, or re-run `rift init` on a registered root to restore its marker. If the registry and filesystem are irreconcilable, unregister with `rift remove -f` on the source root (after backing up) and re-initialize.

## Copy-on-write unavailable (`cow_unavailable`)

Rift has no byte-copy fallback. If no platform strategy can clone the tree, `create` and `init` fail with `copy-on-write cloning unavailable`.

### Platform matrix

| Platform | Supported backends | Failure when |
| --- | --- | --- |
| Linux x64 (btrfs) | Writable subvolume snapshots; reflink import for filtered copies | Path not on btrfs; ordinary directory used for `create` without prior `init` |
| Linux x64 (other, e.g. XFS) | Per-file `FICLONE` reflinks | Filesystem lacks reflink support; probe file creation fails |
| macOS arm64 / x64 | APFS `clonefile` | `clonefile` syscall fails (non-APFS volume, permissions) |
| Windows x64 | None implemented | Any `create` or `init` that requires cloning |

<Warning>
On Windows, the `rift-snapshot` package is published but workspace creation is not implemented. `create` returns `cow_unavailable` with message `no copy-on-write strategy has been implemented for this platform`.
</Warning>

### Linux btrfs-specific failures

| Message fragment | Cause | Fix |
| --- | --- | --- |
| `is not on a btrfs filesystem` | `rift init` on ext4, NFS, etc. | Move workspace to btrfs, or use a reflink-capable filesystem (XFS with `FICLONE`) |
| `Linux snapshot creation requires btrfs` | `create` on btrfs path that is not a subvolume | Run `rift init` first |
| `failed to activate initialized workspace` | Subvolume swap failed mid-conversion | Check permissions and disk space; original may be restored or left in `.rift-init-original-*` staging |

### Cross-filesystem storage

On reflink-capable Linux filesystems, source and destination must share a device:

```
Linux reflinks require source and destination on the same filesystem: /path/to/dest
```

**Fix:** Use default storage (sibling `.rifts/<workspace-name>/`) or pass `--into` on the same mount as the source. This commonly affects workspaces at filesystem mount roots where the default sibling path crosses devices.

### Unsupported file types (`unsupported_entry`)

**Symptom:** `unsupported filesystem entry: /path/to/fifo` (or socket, device node).

**Cause:** Filtered and exact copy walks encounter non-regular entries (FIFOs, sockets) that cannot be reflinked or cloned.

**Fix:** Remove or relocate special files before `rift create`. Partial copy failures roll back: the destination directory is removed and no registry row is inserted.

## Unsafe Git state (`unsafe_git`)

`rift init` and `rift create` call `check_source` on Git repositories. Creation is refused when the copy would be ambiguous or unsafe.

### Rejected conditions

| Condition | Error detail |
| --- | --- |
| Linked Git worktree | `linked Git worktree sources are not supported` (`.git` is a file, not a directory) |
| Merge in progress | `Git state in progress: MERGE_HEAD` |
| Cherry-pick in progress | `Git state in progress: CHERRY_PICK_HEAD` |
| Revert in progress | `Git state in progress: REVERT_HEAD` |
| Bisect in progress | `Git state in progress: BISECT_LOG` |
| Rebase in progress | `Git state in progress: rebase-merge` or `rebase-apply` |
| Index or HEAD locked | `Git state in progress: index.lock` or `HEAD.lock` |

**Fix:** Finish or abort the in-progress Git operation, then retry. For linked worktrees, run Rift from the main worktree whose `.git` is a directory, not from a linked checkout.

<Note>
Repositories with no commits yet are allowed. `HEAD` stays in unborn-branch state because there is no commit to detach to.
</Note>

## Postcreate hook failures (`hook_failed`)

Hooks run **after** the workspace is copied, registered, and Git-prepared. A hook failure does **not** roll back the workspace.

**Symptom:**

```
postcreate hook failed at /path/to/child: `pnpm install` exited with exit status: 1
```

**Behavior:**

- Hooks execute sequentially; the first failure stops later hooks.
- The created workspace remains on disk and in the registry.
- Environment variables available: `RIFT_SOURCE`, `RIFT_DESTINATION`, `RIFT_ID`, `RIFT_PARENT_ID`.
- Stdio is inherited from the parent process.

**Fix options:**

1. Fix the failing command and run it manually in the new workspace.
2. Remove the broken rift: `rift remove` then `rift create --no-hooks` to skip hooks.
3. Correct `.rift.toml` before the next create.

### Invalid configuration (`invalid_config`)

Config is validated **before** copying when hooks are enabled (default). Invalid `.rift.toml` prevents workspace creation entirely.

| Validation rule | Example failure |
| --- | --- |
| `version` must be `1` | `unsupported config version 2` |
| `run` must be non-empty | `postcreate run cannot be empty` |
| Unknown fields rejected | `shell` field in `[[hooks.postcreate]]` |

**Fix:** Correct `.rift.toml` per the configuration reference. Use `rift create --no-hooks` to bypass config loading and hook execution.

## Path, storage, and naming errors

| Error code | Cause | Fix |
| --- | --- | --- |
| `invalid_path` | Not a directory; empty name; `parent/child` name; null byte in path | Pass a canonical directory; use a single path segment for `--name` |
| `already_exists` | Destination or trash target already exists | Pick a different `--name`; run `rift gc` if trash is stale |
| `inside_source` | `--into` or destination falls inside the source tree | Store rifts outside the copied tree (default `.rifts/` sibling or external `--into`) |
| `not_managed` | Ancestor chain broken in registry | Registry corruption or manual DB edit; inspect registry |
| `missing_rift` | Registered path missing on disk during `remove` | Restore moved/deleted directory, or prune with `rift gc` after manual cleanup |

### Root unregistration requires force

Unregistering a source root is CLI-gated:

```
This is the root workspace.

Unregistering it removes Rift metadata and trashes all child rifts.
Run `rift remove -f` to continue.
```

Use `rift remove -f` only when you intend to drop Rift metadata while keeping the source directory. Descendants move to `.trash/`.

## JavaScript and FFI troubleshooting

### Node.js FFI requirements

Node bindings require the experimental FFI API:

```bash
node --experimental-ffi app.mjs
```

With Node's permission model, also pass `--allow-ffi`.

### Missing prebuilt binaries

If `prebuilds/<platform>-<arch>/` is absent after install:

```
Unable to locate the Rift binary for linux-x64. Reinstall rift-snapshot.
```

Reinstall the global package or verify the release archive matches your platform (`darwin-arm64`, `darwin-x64`, `linux-x64`, `windows-x64`).

### FFI protocol errors

| Code | Cause |
| --- | --- |
| `invalid_request` | Malformed JSON request to `rift_ffi_call` |
| `panic` | Unhandled panic inside the native library |
| `serialization` | Response encoding failure |

Catch `RiftError` by `code` for programmatic handling:

```ts
import { create, RiftError } from "rift-snapshot";

try {
  create({ from: process.cwd(), name: "task-a" });
} catch (error) {
  if (error instanceof RiftError) {
    console.error(error.code, error.path, error.message);
  }
}
```

## Diagnostic workflow

<Steps>
<Step title="Reproduce with verbose context">

Note the command, working directory, platform, and filesystem type (btrfs, XFS, APFS). Check whether `.rift` exists and what it contains.

</Step>
<Step title="Classify the error family">

Match stderr or `RiftError.code` to workspace resolution, CoW, Git, hooks, or path/storage categories using the tables above.

</Step>
<Step title="Apply the targeted fix">

Run `rift init` for marker or btrfs subvolume issues. Resolve Git state before `create`. Use `--into` for cross-filesystem storage. Use `--no-hooks` to isolate hook problems.

</Step>
<Step title="Verify recovery">

```bash
rift list          # confirms registry sees children
rift ancestors     # confirms parent chain
rift gc            # cleans stale trash after manual fixes
```

</Step>
</Steps>

## Quick reference: error codes

| Code | Category |
| --- | --- |
| `workspace_not_initialized` | No `.rift` ancestry |
| `missing_marker` | Registry row without `.rift` |
| `initialization_required` | Btrfs source not yet a subvolume |
| `unknown_marker` / `marker_mismatch` | Marker/registry inconsistency |
| `cow_unavailable` | Platform or filesystem unsupported |
| `unsupported_entry` | FIFO, socket, or special file in tree |
| `unsafe_git` | Linked worktree or in-progress Git operation |
| `hook_failed` | Postcreate command failed (workspace kept) |
| `invalid_config` | `.rift.toml` validation failed |
| `already_exists` / `inside_source` / `invalid_path` | Destination or naming problem |
| `missing_rift` / `not_managed` | Filesystem/registry drift on removal |

For the full catalog including path-bearing variants and FFI message shapes, see the error codes reference.

## Related pages

<CardGroup>
<Card title="Initialize a workspace" href="/initialize-workspace">
Run `rift init`, restore missing markers, and convert btrfs directories to subvolumes.
</Card>
<Card title="Copy strategies and platforms" href="/copy-strategies">
Platform backends, reflink requirements, and why byte-copy is not a fallback.
</Card>
<Card title="Git integration" href="/git-integration">
Detached HEAD behavior, marker exclusion, and unsafe source detection.
</Card>
<Card title="Postcreate hooks" href="/postcreate-hooks">
Hook configuration, environment variables, and failure semantics.
</Card>
<Card title="Error codes" href="/error-codes">
Complete `RiftError` catalog for CLI, FFI, and TypeScript bindings.
</Card>
</CardGroup>
