Use named ErrorCode variants for ACP JSON-RPC errors

Replace literal JSON-RPC error codes with the schema's named ErrorCode variants, constructed via Error::new(ErrorCode::X.into(), msg) to keep each custom message: -32603 -> InternalError, -32602 -> InvalidParams, -32002 -> ResourceNotFound. Test assertions keep the numeric form via i32::from(error.code). Co-Authored-By: siGit Code v1.6.6-acp <noreply@sigit.si>

Seto Elkahfi committed Oct 11, 2026 at 14:16 UTC b7b9c6660c95d3cf0b661aa0d97da26e69638a7c
1 file changed +43 -29
src/main.rs
+43 -29
@@ -80,13 +80,13 @@ use agent_client_protocol::schema::v1::{
80 ConfigOptionUpdate, ContentBlock, ContentChunk, CreateElicitationRequest,
81 CreateTerminalRequest, CurrentModeUpdate, DeleteSessionRequest, DeleteSessionResponse, Diff,
82 ElicitationAction, ElicitationRequestScope, ElicitationUrlMode, EmbeddedResourceResource,
83 - ForkSessionRequest, ForkSessionResponse, Implementation, InitializeRequest, InitializeResponse,
84 - KillTerminalRequest, ListSessionsRequest, ListSessionsResponse, LoadSessionRequest,
85 - LoadSessionResponse, LogoutCapabilities, LogoutRequest, LogoutResponse, McpCapabilities,
86 - McpServer, MessageId, Meta, NewSessionRequest, NewSessionResponse, PermissionOption,
87 - PermissionOptionKind, Plan, PlanEntry, PlanEntryPriority, PlanEntryStatus, PromptCapabilities,
88 - PromptRequest, PromptResponse, ReadTextFileRequest, ReleaseTerminalRequest, RequestId,
89 - RequestPermissionOutcome, RequestPermissionRequest, ResumeSessionRequest,
83 + ErrorCode, ForkSessionRequest, ForkSessionResponse, Implementation, InitializeRequest,
84 + InitializeResponse, KillTerminalRequest, ListSessionsRequest, ListSessionsResponse,
85 + LoadSessionRequest, LoadSessionResponse, LogoutCapabilities, LogoutRequest, LogoutResponse,
86 + McpCapabilities, McpServer, MessageId, Meta, NewSessionRequest, NewSessionResponse,
87 + PermissionOption, PermissionOptionKind, Plan, PlanEntry, PlanEntryPriority, PlanEntryStatus,
88 + PromptCapabilities, PromptRequest, PromptResponse, ReadTextFileRequest, ReleaseTerminalRequest,
89 + RequestId, RequestPermissionOutcome, RequestPermissionRequest, ResumeSessionRequest,
90 ResumeSessionResponse, SessionAdditionalDirectoriesCapabilities, SessionCapabilities,
91 SessionCloseCapabilities, SessionConfigOption, SessionConfigOptionCategory,
92 SessionConfigSelectOption, SessionConfigValueId, SessionDeleteCapabilities,
@@ -1431,7 +1431,7 @@ impl SiGitAgent {
1431 // already done — might be a stored error from earlier
1432 if let Some(err) = self.model_load_error.lock().unwrap().as_ref() {
1433 return Err(agent_client_protocol::Error::new(
1434 - -32603,
1434 + ErrorCode::InternalError.into(),
1435 format!("model load failed: {err}"),
1436 ));
1437 }
@@ -1569,7 +1569,7 @@ impl SiGitAgent {
1569 .ok();
1570
1571 return Err(agent_client_protocol::Error::new(
1572 - -32603,
1572 + ErrorCode::InternalError.into(),
1573 format!("model load failed: {err}"),
1574 ));
1575 }
@@ -1865,7 +1865,7 @@ impl SiGitAgent {
1865 let (new_config, max_tokens, new_tool_calling) = resolve_model_config(model_id)
1866 .ok_or_else(|| {
1867 agent_client_protocol::Error::new(
1868 - -32602,
1868 + ErrorCode::InvalidParams.into(),
1869 format!("unknown or unavailable model: {model_id}"),
1870 )
1871 })?;
@@ -1917,10 +1917,18 @@ impl SiGitAgent {
1917
1918 result_rx
1919 .await
1920 - .map_err(|_| agent_client_protocol::Error::new(-32603, "model loader thread crashed"))?
1920 + .map_err(|_| {
1921 + agent_client_protocol::Error::new(
1922 + ErrorCode::InternalError.into(),
1923 + "model loader thread crashed",
1924 + )
1925 + })?
1926 .map_err(|error| {
1927 log::error!("model switch failed: {error}");
1923 - agent_client_protocol::Error::new(-32603, format!("model switch failed: {error}"))
1928 + agent_client_protocol::Error::new(
1929 + ErrorCode::InternalError.into(),
1930 + format!("model switch failed: {error}"),
1931 + )
1932 })?;
1933
1934 self.startup_model_load_started
@@ -2173,13 +2181,13 @@ fn validate_session_roots(
2181 ) -> agent_client_protocol::Result<()> {
2182 if !cwd.is_absolute() {
2183 return Err(agent_client_protocol::Error::new(
2176 - -32602,
2184 + ErrorCode::InvalidParams.into(),
2185 format!("cwd must be an absolute path, got {}", cwd.display()),
2186 ));
2187 }
2188 if let Some(relative) = additional.iter().find(|dir| !dir.is_absolute()) {
2189 return Err(agent_client_protocol::Error::new(
2182 - -32602,
2190 + ErrorCode::InvalidParams.into(),
2191 format!(
2192 "additionalDirectories entries must be absolute paths, got {}",
2193 relative.display()
@@ -2343,7 +2351,7 @@ impl SiGitAgent {
2351
2352 if req.method_id.0.as_ref() != AUTH_METHOD_ID {
2353 return Err(agent_client_protocol::Error::new(
2346 - -32602,
2354 + ErrorCode::InvalidParams.into(),
2355 format!(
2356 "unknown auth method {}; the only one offered is {AUTH_METHOD_ID}",
2357 req.method_id.0
@@ -2366,7 +2374,7 @@ impl SiGitAgent {
2374 let flow = browser_auth::begin();
2375 if !flow.is_loopback() {
2376 return Err(agent_client_protocol::Error::new(
2369 - -32603,
2377 + ErrorCode::InternalError.into(),
2378 "Could not open a local port to receive the sign-in. \
2379 Run `sigit login` in a terminal instead."
2380 .to_string(),
@@ -2389,7 +2397,7 @@ impl SiGitAgent {
2397 Ok(AuthenticateResponse::default())
2398 }
2399 Err(reason) => Err(agent_client_protocol::Error::new(
2392 - -32603,
2400 + ErrorCode::InternalError.into(),
2401 format!(
2402 "Sign-in did not complete ({reason}). \
2403 Try again, or create an account at https://sigit.si."
@@ -2573,7 +2581,7 @@ impl SiGitAgent {
2581
2582 let Some(state) = self.sessions.lock().unwrap().get(&key).cloned() else {
2583 return Err(agent_client_protocol::Error::new(
2576 - -32002,
2584 + ErrorCode::ResourceNotFound.into(),
2585 format!(
2586 "unknown session {key}; create it with session/new or restore it with session/load"
2587 ),
@@ -2837,7 +2845,7 @@ impl SiGitAgent {
2845 && !cwd.is_absolute()
2846 {
2847 return Err(agent_client_protocol::Error::new(
2840 - -32602,
2848 + ErrorCode::InvalidParams.into(),
2849 format!("cwd must be an absolute path, got {}", cwd.display()),
2850 ));
2851 }
@@ -2960,7 +2968,7 @@ impl SiGitAgent {
2968 let Some(history) = session_store::load(&session_key).or(in_memory) else {
2969 log::warn!("{method}: no session {session_key}");
2970 return Err(agent_client_protocol::Error::new(
2963 - -32002,
2971 + ErrorCode::ResourceNotFound.into(),
2972 format!("session {session_key} not found"),
2973 ));
2974 };
@@ -3624,7 +3632,10 @@ impl SiGitAgent {
3632 let snapshot = backend.history_snapshot().await;
3633 self.persist_session(cx, &session_id, &snapshot).await;
3634 self.finish_prompt(&session_id, &cancellation);
3627 - return Err(agent_client_protocol::Error::new(-32603, error));
3635 + return Err(agent_client_protocol::Error::new(
3636 + ErrorCode::InternalError.into(),
3637 + error,
3638 + ));
3639 }
3640 };
3641
@@ -4010,7 +4021,10 @@ impl SiGitAgent {
4021 let _ = self.send_plan_update(cx, session_id.clone(), plan);
4022 }
4023 self.finish_prompt(&session_id, &cancellation);
4013 - return Err(agent_client_protocol::Error::new(-32603, error));
4024 + return Err(agent_client_protocol::Error::new(
4025 + ErrorCode::InternalError.into(),
4026 + error,
4027 + ));
4028 }
4029 };
4030 self.send_usage(cx, &session_id, backend.as_ref(), &result, context_window)
@@ -4479,7 +4493,7 @@ impl SiGitAgent {
4493 return Ok(());
4494 }
4495 Err(agent_client_protocol::Error::new(
4482 - -32002,
4496 + ErrorCode::ResourceNotFound.into(),
4497 format!(
4498 "unknown session {key}; create it with session/new or restore it with session/load"
4499 ),
@@ -4526,7 +4540,7 @@ impl SiGitAgent {
4540 let key = session_id.to_string();
4541 let Some(message) = mode_id.and_then(|id| apply_permission_mode(&key, id)) else {
4542 return Err(agent_client_protocol::Error::new(
4529 - -32602,
4543 + ErrorCode::InvalidParams.into(),
4544 format!("unknown permission mode: {mode_id:?}"),
4545 ));
4546 };
@@ -4594,14 +4608,14 @@ impl SiGitAgent {
4608 (_, Some(LOCAL_INFERENCE_OFF)) => false,
4609 (_, other) => {
4610 return Err(agent_client_protocol::Error::new(
4597 - -32602,
4611 + ErrorCode::InvalidParams.into(),
4612 format!("unknown Local Inference value: {other:?}"),
4613 ));
4614 }
4615 };
4616 if let Err(error) = settings::set_local_inference(enabled) {
4617 return Err(agent_client_protocol::Error::new(
4604 - -32603,
4618 + ErrorCode::InternalError.into(),
4619 format!("could not save Local Inference setting: {error}"),
4620 ));
4621 }
@@ -4620,14 +4634,14 @@ impl SiGitAgent {
4634
4635 if args.config_id.0.as_ref() != MODEL_CONFIG_ID {
4636 return Err(agent_client_protocol::Error::new(
4623 - -32602,
4637 + ErrorCode::InvalidParams.into(),
4638 format!("unknown config option: {}", args.config_id.0),
4639 ));
4640 }
4641
4642 let Some(model_id) = args.value.as_value_id().map(|v| v.0.as_ref()) else {
4643 return Err(agent_client_protocol::Error::new(
4630 - -32602,
4644 + ErrorCode::InvalidParams.into(),
4645 format!(
4646 "expected a value-id for {MODEL_CONFIG_ID}, got {:?}",
4647 args.value
@@ -5481,7 +5495,7 @@ async fn exec_slash_acp(
5495 let mut sessions = agent.sessions.lock().unwrap();
5496 let state = sessions.get_mut(&session_key).ok_or_else(|| {
5497 agent_client_protocol::Error::new(
5484 - -32602,
5498 + ErrorCode::InvalidParams.into(),
5499 format!("cannot clear unknown session {session_key}"),
5500 )
5501 })?;