Skip to main content

track/utils/
error.rs

1//! Error types for the track CLI application.
2//!
3//! This module defines all error types that can occur during track operations,
4//! including database errors, JJ errors, validation errors, and user-facing error messages.
5
6use thiserror::Error;
7
8/// Main error type for the track CLI application.
9///
10/// This enum encompasses all possible errors that can occur during track operations.
11/// Each variant provides a descriptive error message and may contain additional context.
12#[derive(Error, Debug)]
13pub enum TrackError {
14    /// Database operation failed
15    #[error("Database error: {0}")]
16    Database(#[from] rusqlite::Error),
17
18    #[error("No active task. Run 'track new' or 'track switch' first.")]
19    NoActiveTask,
20
21    #[error("Task #{0} not found")]
22    TaskNotFound(i64),
23
24    #[error("Task #{0} is archived")]
25    TaskArchived(i64),
26
27    #[error("Task name cannot be empty")]
28    EmptyTaskName,
29
30    #[error(
31        "track sync is deprecated in JJ mode. Use `jj-task start {slug}` instead (or `track sync --legacy` for old per-TODO worktrees)."
32    )]
33    SyncUseJjTask { slug: String },
34
35    #[error("--worktree was removed. Use one jj-task workspace per task (`jj-task start <slug>`). See `track llm-help`.")]
36    WorktreeFlagRemoved,
37
38    #[error("TODO content cannot be empty")]
39    EmptyTodoContent,
40
41    #[error("Scrap content cannot be empty")]
42    EmptyScrapContent,
43
44    #[error("Workspaces have uncommitted changes: {0:?}")]
45    UncommittedWorkspaces(Vec<String>),
46
47    #[error(
48        "jj-task workspace '{slug}' is not complete — run `jj-task done {slug}` after merging your PR. Active workspaces: {workspaces:?}"
49    )]
50    JjTaskNotCompleted {
51        slug: String,
52        workspaces: Vec<String>,
53    },
54
55    #[error("Ticket '{0}' is already linked to task #{1}")]
56    DuplicateTicket(String, i64),
57
58    #[error("Invalid ticket ID format: {0}")]
59    InvalidTicketFormat(String),
60
61    #[error("TODO #{0} not found")]
62    TodoNotFound(i64),
63
64    #[error("Worktree #{0} not found")]
65    WorktreeNotFound(i64),
66
67    #[error("Invalid status: {0}")]
68    InvalidStatus(String),
69
70    #[error("Cannot transition from '{from}' to '{to}'")]
71    InvalidStatusTransition { from: String, to: String },
72
73    #[error("TODO cannot be reopened from '{from}'. Add a new TODO instead.")]
74    TodoReopenForbidden { from: String },
75
76    #[error("Use 'track todo done <id>' to complete a TODO (merges JJ workspace if present).")]
77    TodoCompleteRequiresDoneCommand,
78
79    #[error(
80        "Workspace was merged (bookmark: {bookmark}) but failed to mark TODO #{todo_index} as done: {detail}"
81    )]
82    TodoCompletionDbFailed {
83        todo_index: i64,
84        bookmark: String,
85        detail: String,
86    },
87
88    #[error("JJ error: {0}")]
89    Jj(String),
90
91    #[error("Path '{0}' is not a JJ repository")]
92    NotJjRepository(String),
93
94    #[error("Bookmark '{0}' already exists")]
95    BookmarkExists(String),
96
97    #[error("Invalid URL format: {0}")]
98    InvalidUrl(String),
99
100    #[error("No repositories registered for this task")]
101    NoRepositoriesRegistered,
102
103    #[error("Failed to check status for {0}")]
104    FailedRepoStatusCheck(String),
105
106    #[error("Repository {0} has pending changes in the base workspace. Please clean before sync.")]
107    RepoHasPendingChanges(String),
108
109    #[error("IO error: {0}")]
110    Io(#[from] std::io::Error),
111
112    #[error("Operation cancelled by user")]
113    #[allow(dead_code)]
114    Cancelled,
115
116    #[error("Failed to resolve path: {0}")]
117    PathResolutionFailed(String),
118
119    #[error("Current directory is not a registered repository for this task")]
120    CurrentDirectoryNotRegistered,
121
122    #[error("Workspace {path} has uncommitted changes. Use --force to recreate.")]
123    WorkspaceHasUncommittedChanges { path: String },
124
125    #[error("Bookmark '{bookmark}' not found in {repo_path}")]
126    BookmarkNotFound { bookmark: String, repo_path: String },
127
128    #[error("No workspace paths available for this TODO")]
129    NoWorkspacePathsAvailable,
130
131    #[error("Workspace cleanup failed: {0:?}")]
132    WorkspaceRemovalFailed(Vec<String>),
133
134    #[error("Failed to check workspace status for {path}: {detail}")]
135    WorkspaceStatusCheckFailed { path: String, detail: String },
136
137    #[error("JSON serialization failed: {0}")]
138    SerializationFailed(String),
139
140    #[error("TODO #{0} not found in current task")]
141    TodoIndexNotFound(i64),
142
143    #[error("No pending TODOs to reorder")]
144    NoPendingTodos,
145
146    #[error("TODO #{0} is not among pending TODOs")]
147    TodoNotPending(i64),
148
149    #[error("Link #{0} not found")]
150    LinkNotFound(i64),
151
152    #[error("No task found with reference '{0}'")]
153    TaskReferenceNotFound(String),
154
155    #[error("Alias '{alias}' is already in use by task #{task_id}")]
156    AliasInUse { alias: String, task_id: i64 },
157
158    #[error("Invalid alias: {0}")]
159    InvalidAlias(String),
160
161    #[error("Repository already registered for this task")]
162    RepoAlreadyRegistered,
163
164    #[error("Repository #{0} not found")]
165    TaskRepoNotFound(i64),
166
167    #[error("Repository #{0} not found in current task")]
168    TaskRepoIndexNotFound(i64),
169
170    #[error("Link #{0} not found in current task")]
171    LinkIndexNotFound(i64),
172
173    #[error("Path '{0}' is not a git repository")]
174    NotGitRepository(String),
175
176    #[error("Git error: {0}")]
177    Git(String),
178
179    #[error("Invalid jj-task workspace map at {path}: {detail}")]
180    JjTaskMapInvalid { path: String, detail: String },
181
182    #[error("Template '{name}' render failed: {detail}")]
183    TemplateRenderFailed { name: String, detail: String },
184
185    #[error("Failed to determine data directory")]
186    DataDirectoryUnavailable,
187
188    #[error("Invalid VCS mode: {0}")]
189    InvalidVcsMode(String),
190
191    #[error("Invalid app_state value for '{key}': {detail}")]
192    InvalidAppStateValue { key: String, detail: String },
193
194    #[error("Migration blocked: {detail}")]
195    MigrationBlocked { detail: String },
196
197    #[error("Unknown config key '{0}' (supported: vcs-mode)")]
198    UnknownConfigKey(String),
199
200    #[error("{0}")]
201    Other(String),
202}
203
204/// Convenience type alias for Results with TrackError.
205///
206/// This type is used throughout the application for operations that may fail.
207pub type Result<T> = std::result::Result<T, TrackError>;