From 000f69cb32d8a7d27d1213b7817fb610637b9397 Mon Sep 17 00:00:00 2001 From: Kirtiman Mishra <110175055+kirtimanmishrazipstack@users.noreply.github.com> Date: Wed, 24 Jun 2026 21:29:37 +0530 Subject: [PATCH 1/2] UN-3621 [HOTFIX] Structure tool no longer crashes when single-pass extraction returns a non-object output (#2110) UN-3621 [FIX] Guard structure pipeline against non-dict single-pass output Single-pass extraction can return a top-level JSON array (e.g. a truncated/ runaway LLM response that hit its output-token cap). The parsed `output` is then a list, and _handle_structure_pipeline called `.values()` on it unconditionally, raising an opaque `AttributeError: 'list' object has no attribute 'values'` that failed the file with no actionable signal. Guard the output shape: if it isn't a dict, return a clear ExecutionResult failure naming the likely cause instead of crashing, and stop the malformed payload from flowing downstream as a success. Co-authored-by: Claude Opus 4.8 (1M context) --- workers/executor/executors/legacy_executor.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/workers/executor/executors/legacy_executor.py b/workers/executor/executors/legacy_executor.py index eae4d05b2f..ce7fbea0d1 100644 --- a/workers/executor/executors/legacy_executor.py +++ b/workers/executor/executors/legacy_executor.py @@ -703,6 +703,19 @@ def _failure(child_result: ExecutionResult) -> ExecutionResult: ) output_map = structured_output.get(PSKeys.OUTPUT, {}) or {} + if not isinstance(output_map, dict): + # Single-pass can return a non-dict (e.g. a top-level JSON array) when the + # LLM emits a truncated/runaway response. Fail clearly instead of crashing + # on .values(), and keep the malformed shape from flowing downstream as a + # success. + return ExecutionResult.failure( + error=( + f"Single-pass extraction returned a {type(output_map).__name__}, " + "expected a field map; the LLM likely produced a truncated or " + "runaway response (check output-token usage)." + ), + metadata={"usage_records": pipeline_records}, + ) answered = sum(1 for v in output_map.values() if v not in (None, "", [], {})) shim.stream_log(f"Pipeline completed: {answered}/{len(outputs)} prompts answered") out_metadata = { From 8b3243cac4031bc6b6a85b875990b060a88ff963 Mon Sep 17 00:00:00 2001 From: Deepak K <89829542+Deepak-Kesavan@users.noreply.github.com> Date: Wed, 24 Jun 2026 23:34:34 +0530 Subject: [PATCH 2/2] [HOTFIX] Raise URLValidator max_length to 8192 for long S3 presigned URLs (#2111) --- backend/backend/settings/base.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/backend/settings/base.py b/backend/backend/settings/base.py index 7593305fdd..970b5fac6a 100644 --- a/backend/backend/settings/base.py +++ b/backend/backend/settings/base.py @@ -15,10 +15,17 @@ from urllib.parse import quote import httpx +from django.core.validators import URLValidator from dotenv import find_dotenv, load_dotenv from utils.common_utils import CommonUtils from utils.cors_origin import normalize_web_app_origin +# Django 5.0+ caps URLValidator at 2048 chars. S3 pre-signed URLs signed with +# temporary/STS credentials (carrying X-Amz-Security-Token) routinely exceed this, +# causing "Enter a valid URL." on the API deployment `presigned_urls` field. +# Raise the cap globally. No-op on Django 4.2.x (no such attribute is checked). +URLValidator.max_length = 8192 + missing_settings = []