1use thiserror::Error;
7
8#[derive(Error, Debug)]
13pub enum TrackError {
14 #[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
204pub type Result<T> = std::result::Result<T, TrackError>;