fix: preserve Workdir operation error classification

This commit is contained in:
2026-08-30 20:07:46 +09:00
parent a7bf5ceac3
commit a9aa09636f
5 changed files with 311 additions and 32 deletions
@@ -195,11 +195,16 @@ impl WorkspaceAttachedWorkdirSession {
.execute(request)
.map_err(workspace_workdir_error)?;
if !response.is_success() {
return Err(WorkdirError::Transport(format!(
"Workspace Workdir API returned HTTP {}: {}",
response.status,
bounded_error_body(&response.body)
)));
return Err(
serde_json::from_str::<workdir::http::WorkdirTransportError>(&response.body)
.map(workdir::http::WorkdirTransportError::into_workdir_error)
.unwrap_or_else(|_| {
WorkdirError::Transport(format!(
"Workspace Workdir operation failed with HTTP {}",
response.status
))
}),
);
}
serde_json::from_str(&response.body).map_err(|error| {
WorkdirError::Transport(format!(
@@ -795,6 +800,21 @@ mod tests {
}
}
fn error_response(
status: u16,
code: workdir::http::WorkdirTransportErrorCode,
message: &str,
) -> WorkspaceResponse {
WorkspaceResponse {
status,
body: serde_json::to_string(&workdir::http::WorkdirTransportError {
code,
message: message.to_string(),
})
.unwrap(),
}
}
fn workdir_json(id: &str) -> serde_json::Value {
json!({
"working_directory_id": id,
@@ -1175,6 +1195,48 @@ mod tests {
assert_eq!(validation["delegations"].as_array().unwrap().len(), 1);
}
#[tokio::test]
async fn attached_session_preserves_typed_provider_validation_error() {
let client = Arc::new(RecordingWorkspaceClient::new(vec![error_response(
400,
workdir::http::WorkdirTransportErrorCode::InvalidRequest,
"Workdir operation request is invalid",
)]));
let session = WorkspaceAttachedWorkdirSession::handle(client);
let error = session
.glob(workdir::GlobRequest {
pattern: "[".to_string(),
path: workdir::WorkdirPath::root(),
limit: 10,
})
.await
.unwrap_err();
assert!(matches!(error, WorkdirError::InvalidArgument(_)));
}
#[tokio::test]
async fn attached_session_does_not_expose_untyped_workspace_error_body() {
let client = Arc::new(RecordingWorkspaceClient::new(vec![WorkspaceResponse {
status: 502,
body: "secret token and /host/private/path".to_string(),
}]));
let session = WorkspaceAttachedWorkdirSession::handle(client);
let error = session
.stat(StatRequest {
path: workdir::WorkdirPath::root(),
})
.await
.unwrap_err();
let message = error.to_string();
assert!(matches!(error, WorkdirError::Transport(_)));
assert!(!message.contains("secret token"));
assert!(!message.contains("/host/private/path"));
assert!(message.contains("HTTP 502"));
}
#[tokio::test]
async fn nested_attached_session_preserves_full_delegation_chain() {
let client = Arc::new(RecordingWorkspaceClient::new(vec![