This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-8885-ee1deb75fb16f0ead6c715fa9e52465157f85c51 in repository https://gitbox.apache.org/repos/asf/texera.git
commit ed724ebede6dba46f778e2adfd471ee07dcc251e Author: Prateek Ganigi <[email protected]> AuthorDate: Wed Oct 14 23:45:58 2026 +0000 fix(workflow-operator): require b64_json to be a string before building a data URL (#8885) ### What changes were proposed in this PR? `ImageTaskCodegen.parsePython`'s `image-to-image` branch built a data URL from the provider's `b64_json` without checking it, so a malformed but valid-JSON 200 put an unrenderable value in the result column, a null field became the literal `data:image/png;base64,None`, with nothing to indicate why the image was blank. `b64_json` must now be a non-empty string; anything else falls through to `json.dumps(body)`, which is what the branch already did for unrecognised shapes. Scoped deliberately to `b64_json`, matching the issue. The sibling `url` lookups in the same branch have a comparable gap and are left for a separate issue. Not addressed: a `b64_json` that is a string but not valid base64 still yields an unrenderable data URL. Detecting that means decoding the value on every row, and corrupt-but-well-formed base64 would pass regardless, so the browser's rejection is unchanged either way. Same reasoning as #8872. ### Any related issues? Closes #8870 ### How was this PR tested? Written test-first: a test asserting the string guard was added to `ImageTaskCodegenSpec` and fails against the current parse. The suite is at 155 tests, `PythonCodeRawInvalidTextSpec` py-compiles all 117 operators, and `scalafmtCheck` and `scalafix --check` are clean. The generated parser was also run directly: a null or numeric `b64_json` now returns the serialized body, and a valid base64 payload still produces the same data URL as before. ### Was this PR authored or co-authored using generative AI tooling? Yes, this PR was co-authored with Claude in compliance with ASF policy. Co-authored-by: Xuan Gu <[email protected]> --- .../amber/operator/huggingFace/codegen/ImageTaskCodegen.scala | 8 ++++++-- .../operator/huggingFace/codegen/ImageTaskCodegenSpec.scala | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/codegen/ImageTaskCodegen.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/codegen/ImageTaskCodegen.scala index 201d7b1623..6662878b13 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/codegen/ImageTaskCodegen.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/codegen/ImageTaskCodegen.scala @@ -143,8 +143,12 @@ object ImageTaskCodegen extends TaskCodegen { | if outputs and isinstance(outputs[0], str) and outputs[0].startswith("http"): | return self._url_to_data_url(outputs[0]) | if isinstance(data, list) and data and isinstance(data[0], dict): - | if "b64_json" in data[0]: - | return f"data:image/png;base64,{data[0]['b64_json']}" + | # b64_json comes straight from the provider: an unchecked + | # value reaches the cell as a data URL nothing can render, + | # e.g. "data:image/png;base64,None". + | b64 = data[0].get("b64_json") + | if isinstance(b64, str) and b64: + | return f"data:image/png;base64,{b64}" | if "url" in data[0]: | return self._url_to_data_url(data[0]["url"]) | return json.dumps(body) diff --git a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/codegen/ImageTaskCodegenSpec.scala b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/codegen/ImageTaskCodegenSpec.scala index 0e026fdf0f..81518d8d0c 100644 --- a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/codegen/ImageTaskCodegenSpec.scala +++ b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/codegen/ImageTaskCodegenSpec.scala @@ -111,6 +111,15 @@ class ImageTaskCodegenSpec extends AnyFlatSpec with Matchers { out should include("data:image/png;base64,") } + // #8870: the image-to-image branch trusted whatever the provider returned, so a + // malformed 200 produced a data URL nothing can render — a null b64_json became + // the literal "data:image/png;base64,None". + it should "require b64_json to be a string before building a data URL" in { + val out = ImageTaskCodegen.parsePython(makeCtx()) + out should include("""isinstance(b64, str)""") + out should not include ("""f"data:image/png;base64,{data[0]['b64_json']}"""") + } + it should "fall back to json.dumps(body) for structured tasks" in { val out = ImageTaskCodegen.parsePython(makeCtx()) out should include("json.dumps(body)")
