1use thiserror::Error;
8
9#[derive(Debug, Error)]
10pub enum OneharnessError {
11 #[error("no harness selected: pass --all or --harness <id>, or set `all`/`harnesses` in oneharness.toml (see `oneharness list`)")]
12 NoSelection,
13
14 #[error("unknown harness id `{id}`. valid ids: {valid}")]
15 UnknownHarness { id: String, valid: String },
16
17 #[error("no prompt provided: pass --prompt <text> or --prompt-file <path>")]
18 NoPrompt,
19
20 #[error("--prompt-file - (stdin) can be given only once, but it was passed {count} times")]
21 MultipleStdinPrompts { count: usize },
22
23 #[error("stdin ('-') can be read only once, but {count} inputs request it (across --prompt-file and --system-file)")]
24 MultipleStdinConsumers { count: usize },
25
26 #[error("a batch run (more than one prompt) needs exactly one harness (a shared cache prefix is per harness/model), but {count} were selected: {selected}. Select one with --harness <id>")]
27 BatchMultipleHarnesses { count: usize, selected: String },
28
29 #[error("a batch run (more than one prompt) cannot be combined with --resume/--fork (those continue a single session with a single prompt)")]
30 BatchResume,
31
32 #[error("--resume needs exactly one harness (a session belongs to one harness), but {count} were selected: {selected}")]
33 ResumeMultipleHarnesses { count: usize, selected: String },
34
35 #[error("harness `{id}` does not support --resume. supported: {supported}")]
36 ResumeUnsupported { id: String, supported: String },
37
38 #[error("harness `{id}` does not support --fork (it resumes linearly, appending in place). supported: {supported}")]
39 ForkUnsupported { id: String, supported: String },
40
41 #[error("harness `{id}` does not support `--mode {mode}`. supported modes: {supported}")]
42 ModeUnsupported {
43 id: String,
44 mode: String,
45 supported: String,
46 },
47
48 #[error("could not read harness config `{path}`: {source}")]
49 HarnessConfigRead {
50 path: String,
51 #[source]
52 source: std::io::Error,
53 },
54
55 #[error("cannot sync into `{path}`: {message} (oneharness only rewrites files it can parse, so it never destroys content it does not understand)")]
56 HarnessConfigUnmergeable { path: String, message: String },
57
58 #[error("harness `{id}` has no hook mapping, so oneharness cannot install a hook into it")]
59 HookUnsupported { id: String },
60
61 #[error("harness `{id}` has no user-global hook location, so oneharness cannot install a global hook into it")]
62 HookGlobalUnsupported { id: String },
63
64 #[error("cannot resolve the user-global hook directory for `{id}`: {var} is not set")]
65 HookGlobalDirMissing { id: String, var: &'static str },
66
67 #[error("`oneharness sync --global` installs hooks only, but permission rules or `settings` are configured for `{id}` — those are project-scoped (sync them without --global, or remove them)")]
68 GlobalSyncOnlyHooks { id: String },
69
70 #[error(
71 "harness `{id}` has no pre-tool gate, so `oneharness gate {id}` cannot emit a verdict"
72 )]
73 GateUnsupported { id: String },
74
75 #[error("could not read mock rules file `{path}`: {source}")]
76 MockRulesFile {
77 path: String,
78 #[source]
79 source: std::io::Error,
80 },
81
82 #[error("invalid mock rules file `{path}`: {message}")]
83 MockRulesInvalid { path: String, message: String },
84
85 #[error("harness `{id}` cannot express the mock action `{action}`, so `oneharness mock {id}` refuses this ruleset (see `mock_rewrite`/`supports_mock_deny` in `oneharness list`)")]
86 MockActionUnsupported { id: String, action: &'static str },
87
88 #[error(
89 "harness `{id}` cannot take a one-shot mock hook (`--mock-rules`/`--spy-file`): {reason}"
90 )]
91 MockDeliveryUnsupported { id: String, reason: &'static str },
92
93 #[error("cannot embed `{path}` into a hook command: it contains whitespace (hook commands are tokenized on spaces by some harnesses; use space-free paths)")]
94 MockPathWhitespace { path: String },
95
96 #[error("could not wire the one-shot mock hook: {message}")]
97 MockSetup { message: String },
98
99 #[error("could not write harness config `{path}`: {source}")]
100 HarnessConfigWrite {
101 path: String,
102 #[source]
103 source: std::io::Error,
104 },
105
106 #[error("could not read prompt file `{path}`: {source}")]
107 PromptFile {
108 path: String,
109 #[source]
110 source: std::io::Error,
111 },
112
113 #[error("could not read system prompt file `{path}`: {source}")]
114 SystemFile {
115 path: String,
116 #[source]
117 source: std::io::Error,
118 },
119
120 #[error("could not read schema file `{path}`: {source}")]
121 SchemaFile {
122 path: String,
123 #[source]
124 source: std::io::Error,
125 },
126
127 #[error("invalid --schema: {0}")]
128 Schema(String),
129
130 #[error("invalid --stream: {0}")]
131 StreamInvalid(String),
132
133 #[error("could not read config file `{path}`: {source}")]
134 ConfigRead {
135 path: String,
136 #[source]
137 source: std::io::Error,
138 },
139
140 #[error("invalid config file `{path}`: {message}")]
141 ConfigInvalid { path: String, message: String },
142
143 #[error("invalid environment-variable config override: {0}")]
144 EnvConfigInvalid(String),
145
146 #[error("invalid --bin override `{0}`: expected the form ID=PATH")]
147 BadBinOverride(String),
148
149 #[error("invalid --env `{0}`: expected the form KEY=VALUE")]
150 BadEnv(String),
151
152 #[error("could not write output to `{path}`: {source}")]
153 OutputDir {
154 path: String,
155 #[source]
156 source: std::io::Error,
157 },
158
159 #[error("could not access history under `{path}`: {source}")]
160 HistoryIo {
161 path: String,
162 #[source]
163 source: std::io::Error,
164 },
165
166 #[error("no history directory: pass --history-dir, set `history_dir` in config, or ONEHARNESS_HISTORY_DIR (a default under the platform state dir could not be resolved)")]
167 HistoryNoDir,
168
169 #[error("failed to write JSON output: {0}")]
170 Serialize(#[from] serde_json::Error),
171}