Skip to content

Commit c5549e4

Browse files
Hunter Bowncy2311
authored andcommitted
fix: harvest error-message fixes from PR #2933 — better tool denial + subagent conflict messages (#3020)
Three targeted error-message improvements extracted from community PR #2933 (author cy2311), with additional model-not-found annotation: 1. dispatch.rs format_tool_error: pass through self-explanatory messages that already name the cause (mode switch, allow_shell, feature flag, denied by user) instead of appending a conflicting generic suffix. Fixes the Plan-mode double-message (#2657). 2. subagent/mod.rs session-name conflict: include elapsed time (started Ns ago / NmNs ago) so the parent can distinguish a live worker from a stale/failed earlier spawn (#2656). 3. subagent/mod.rs annotate_child_model_error: catch model-not-found patterns (Model Not Exist, does not exist, no such model, etc.) in the raw error text even when the taxonomy classifies them as Internal rather than Authorization/State (#2653). Closes #2653, #2656, #2657. Credit: cy2311 for the dispatch.rs and subagent conflict hunks from #2933. Co-authored-by: cy2311 <cy2311@users.noreply.github.com>
1 parent b23067b commit c5549e4

2 files changed

Lines changed: 56 additions & 6 deletions

File tree

crates/tui/src/core/engine/dispatch.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,37 @@ pub(super) fn format_tool_error(err: &ToolError, tool_name: &str) -> String {
117117
),
118118
ToolError::NotAvailable { message } => {
119119
let lower = message.to_ascii_lowercase();
120-
if lower.contains("current tool catalog") || lower.contains("did you mean:") {
120+
// #3020: Pass through self-explanatory messages that already name the
121+
// cause (mode switch, allow_shell, feature flag). Avoids appending a
122+
// conflicting "Check mode, feature flags" suffix on top of
123+
// "switch to Agent, Goal, or YOLO mode" which confuses the model.
124+
if lower.contains("current tool catalog")
125+
|| lower.contains("did you mean:")
126+
|| lower.contains("mode")
127+
|| lower.contains("allow_shell")
128+
|| lower.contains("feature flag")
129+
{
121130
message.clone()
122131
} else {
123132
format!(
124133
"Tool '{tool_name}' is not available: {message}. Check mode, feature flags, or tool name."
125134
)
126135
}
127136
}
128-
ToolError::PermissionDenied { message } => format!(
129-
"Tool '{tool_name}' was denied: {message}. Adjust approval mode or request permission."
130-
),
137+
ToolError::PermissionDenied { message } => {
138+
let lower = message.to_ascii_lowercase();
139+
// #3020: Pass through messages that already name the denial cause.
140+
if lower.contains("mode")
141+
|| lower.contains("allow_shell")
142+
|| lower.contains("denied by user")
143+
{
144+
message.clone()
145+
} else {
146+
format!(
147+
"Tool '{tool_name}' was denied: {message}. Adjust approval mode or request permission."
148+
)
149+
}
150+
}
131151
}
132152
}
133153

crates/tui/src/tools/subagent/mod.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,8 +1496,19 @@ impl SubAgentManager {
14961496
.values()
14971497
.find(|existing| existing.session_name == name)
14981498
{
1499+
// #3020: Include elapsed time so the parent can distinguish a
1500+
// live worker from a stale/failed earlier spawn (#2656).
1501+
let elapsed = existing.started_at.elapsed();
1502+
let since = if elapsed.as_secs() < 120 {
1503+
format!("{}s ago", elapsed.as_secs())
1504+
} else {
1505+
let mins = elapsed.as_secs() / 60;
1506+
let secs = elapsed.as_secs() % 60;
1507+
format!("{mins}m{secs}s ago")
1508+
};
14991509
return Err(anyhow!(
1500-
"Sub-agent session name '{name}' is already in use by agent_id '{}' (status: {}). \
1510+
"Sub-agent session name '{name}' is already in use by agent_id '{}' \
1511+
(status: {}, started {since}). \
15011512
Reuse that agent_id with agent_eval/agent_close, or open with a different name.",
15021513
existing.id,
15031514
subagent_status_name(&existing.status)
@@ -5619,7 +5630,26 @@ fn annotate_child_model_error(err: &str, model: &str) -> String {
56195630
"{err}\n(child model `{model}` may be unavailable under the current access profile — \
56205631
retry agent_open with a different `model`, or remove `model` to inherit the parent's)"
56215632
),
5622-
_ => err.to_string(),
5633+
_ => {
5634+
// #3020 (#2653): Provider rejections like "Model Not Exist" or
5635+
// "does not exist or you do not have access" often classify as
5636+
// `Internal` rather than `Authorization`/`State`. Catch these
5637+
// patterns in the raw error text and annotate anyway.
5638+
let lower = err.to_ascii_lowercase();
5639+
if lower.contains("model not exist")
5640+
|| lower.contains("model_not_found")
5641+
|| lower.contains("does not exist")
5642+
|| lower.contains("no such model")
5643+
|| lower.contains("invalid model")
5644+
{
5645+
format!(
5646+
"{err}\n(child model `{model}` may be unavailable under the current access profile — \
5647+
retry agent_open with a different `model`, or remove `model` to inherit the parent's)"
5648+
)
5649+
} else {
5650+
err.to_string()
5651+
}
5652+
}
56235653
}
56245654
}
56255655

0 commit comments

Comments
 (0)