Skip to main content

oneharness_core/
errors.rs

1//! Typed errors surfaced only at the application boundary (see `dispatch`).
2//!
3//! These cover usage and configuration faults — the kind that should abort the
4//! process. A harness's own behavior (missing binary, non-zero exit, hang) is
5//! never an error here: it is recorded as data in the JSON report.
6
7use 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}