Require data URLs for code-mode image output (#33659)

## What changed

- Accept image output from `image()` and `generatedImage()` only when its URL
  uses the `data:` scheme.
- Preserve the dedicated error for remote HTTP URLs and report other malformed
  or unsupported image URLs as invalid image output.

## Testing

- Add service-level coverage for rejecting invalid output from both image
  helpers.
- Add an end-to-end code-mode test that verifies the tool call fails instead of
  returning an invalid image item.

GitOrigin-RevId: beaf8c8830574150e8166b6ff5daf7f6dc4dc0a1
This commit is contained in:
rka-oai
2026-07-16 18:28:59 +00:00
committed by copyberry
parent c4ce0493dc
commit 5331d20f6e
3 changed files with 86 additions and 2 deletions
+9 -2
View File
@@ -6,6 +6,8 @@ use codex_code_mode_protocol::ImageDetail;
const IMAGE_HELPER_EXPECTS_MESSAGE: &str = "image expects a non-empty image URL string, an object with image_url and optional detail, or a raw MCP image block";
const REMOTE_IMAGE_URL_ERROR: &str = "Tool call failed: remote image URLs are not supported in tool outputs. Pass a base64 data URI instead";
const INVALID_IMAGE_URL_ERROR: &str =
"Tool call failed: invalid image output. Pass a base64 data URI instead";
const CODEX_IMAGE_DETAIL_META_KEY: &str = "codex/imageDetail";
pub(super) fn serialize_output_text(
@@ -59,10 +61,15 @@ pub(super) fn normalize_output_image(
if image_url.is_empty() {
return Err(IMAGE_HELPER_EXPECTS_MESSAGE.to_string());
}
let lower = image_url.to_ascii_lowercase();
if lower.starts_with("http://") || lower.starts_with("https://") {
let Some((scheme, _)) = image_url.split_once(':') else {
return Err(INVALID_IMAGE_URL_ERROR.to_string());
};
if scheme.eq_ignore_ascii_case("http") || scheme.eq_ignore_ascii_case("https") {
return Err(REMOTE_IMAGE_URL_ERROR.to_string());
}
if !scheme.eq_ignore_ascii_case("data") {
return Err(INVALID_IMAGE_URL_ERROR.to_string());
}
let detail = detail_override.or(detail);
let detail = match detail {
+34
View File
@@ -873,6 +873,40 @@ async fn image_helpers_reject_remote_urls() {
}
}
#[tokio::test]
async fn image_helpers_reject_invalid_image_outputs() {
let image_url =
"Error executing tool exec: Expected at least one message to convert to CallToolResult";
for source in [
format!("image({image_url:?}, \"original\");"),
format!("generatedImage({{ image_url: {image_url:?} }});"),
] {
let service = InProcessCodeModeSession::new();
let response = execute(
&service,
ExecuteRequest {
source,
yield_time_ms: None,
..execute_request("")
},
)
.await;
assert_eq!(
response,
RuntimeResponse::Result {
cell_id: cell_id("1"),
content_items: Vec::new(),
error_text: Some(
"Tool call failed: invalid image output. Pass a base64 data URI instead"
.to_string(),
),
}
);
}
}
#[tokio::test]
async fn image_helper_rejects_unsupported_detail() {
let service = InProcessCodeModeSession::new();