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("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>")]
24    BatchMultipleHarnesses { count: usize, selected: String },
25
26    #[error("a batch run (more than one prompt) cannot be combined with --resume/--fork (those continue a single session with a single prompt)")]
27    BatchResume,
28
29    #[error("--resume needs exactly one harness (a session belongs to one harness), but {count} were selected: {selected}")]
30    ResumeMultipleHarnesses { count: usize, selected: String },
31
32    #[error("harness `{id}` does not support --resume. supported: {supported}")]
33    ResumeUnsupported { id: String, supported: String },
34
35    #[error("harness `{id}` does not support --fork (it resumes linearly, appending in place). supported: {supported}")]
36    ForkUnsupported { id: String, supported: String },
37
38    #[error("harness `{id}` does not support `--mode {mode}`. supported modes: {supported}")]
39    ModeUnsupported {
40        id: String,
41        mode: String,
42        supported: String,
43    },
44
45    #[error("could not read harness config `{path}`: {source}")]
46    HarnessConfigRead {
47        path: String,
48        #[source]
49        source: std::io::Error,
50    },
51
52    #[error("cannot sync into `{path}`: {message} (oneharness only rewrites files it can parse, so it never destroys content it does not understand)")]
53    HarnessConfigUnmergeable { path: String, message: String },
54
55    #[error("harness `{id}` has no hook mapping, so oneharness cannot install a hook into it")]
56    HookUnsupported { id: String },
57
58    #[error("harness `{id}` has no user-global hook location, so oneharness cannot install a global hook into it")]
59    HookGlobalUnsupported { id: String },
60
61    #[error("cannot resolve the user-global hook directory for `{id}`: {var} is not set")]
62    HookGlobalDirMissing { id: String, var: &'static str },
63
64    #[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)")]
65    GlobalSyncOnlyHooks { id: String },
66
67    #[error(
68        "harness `{id}` has no pre-tool gate, so `oneharness gate {id}` cannot emit a verdict"
69    )]
70    GateUnsupported { id: String },
71
72    #[error("could not write harness config `{path}`: {source}")]
73    HarnessConfigWrite {
74        path: String,
75        #[source]
76        source: std::io::Error,
77    },
78
79    #[error("could not read prompt file `{path}`: {source}")]
80    PromptFile {
81        path: String,
82        #[source]
83        source: std::io::Error,
84    },
85
86    #[error("could not read schema file `{path}`: {source}")]
87    SchemaFile {
88        path: String,
89        #[source]
90        source: std::io::Error,
91    },
92
93    #[error("invalid --schema: {0}")]
94    Schema(String),
95
96    #[error("could not read config file `{path}`: {source}")]
97    ConfigRead {
98        path: String,
99        #[source]
100        source: std::io::Error,
101    },
102
103    #[error("invalid config file `{path}`: {message}")]
104    ConfigInvalid { path: String, message: String },
105
106    #[error("invalid environment-variable config override: {0}")]
107    EnvConfigInvalid(String),
108
109    #[error("invalid --bin override `{0}`: expected the form ID=PATH")]
110    BadBinOverride(String),
111
112    #[error("invalid --env `{0}`: expected the form KEY=VALUE")]
113    BadEnv(String),
114
115    #[error("could not write output to `{path}`: {source}")]
116    OutputDir {
117        path: String,
118        #[source]
119        source: std::io::Error,
120    },
121
122    #[error("failed to write JSON output: {0}")]
123    Serialize(#[from] serde_json::Error),
124}