Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
215929d
UN-1924 [FIX] Reject unsupported files by sniffing MIME in API storag…
Deepak-Kesavan Sep 1, 2026
7af4732
UN-1924 [FIX] Terminalise an API execution when every file is rejected
Deepak-Kesavan Sep 1, 2026
63d5237
UN-1924 [FIX] Address review findings on unsupported-file rejection
Deepak-Kesavan Sep 2, 2026
4b436a8
UN-1924 [FIX] Release API rate limit slots by org id, not model instance
Deepak-Kesavan Sep 2, 2026
9929f88
UN-1924 [FIX] Fail one undetectable upload instead of the whole request
Deepak-Kesavan Sep 2, 2026
0efd943
UN-1924 [FIX] Acknowledge results and notify subscribers on an all-re…
Deepak-Kesavan Sep 7, 2026
b27ef4c
UN-1924 [FIX] Keep acknowledgement and notification independent
Deepak-Kesavan Sep 7, 2026
2bf3bb7
UN-1924 [MISC] Drop formatter churn on two untouched assertions
Deepak-Kesavan Sep 7, 2026
cb97bf6
UN-1924 [FIX] Mirror LLMWhisperer's file type gate
Deepak-Kesavan Sep 18, 2026
938a957
Merge remote-tracking branch 'origin/main' into UN-1924-reject-unsupp…
Deepak-Kesavan Sep 18, 2026
6d9f450
UN-1924 [FIX] Recognise a PDF that does not start at offset 0
Deepak-Kesavan Sep 21, 2026
80867ea
Merge remote-tracking branch 'origin/main' into UN-1924-reject-unsupp…
Deepak-Kesavan Sep 21, 2026
5cdce85
UN-1924 [FIX] Harden rejection reporting and the PDF rescue
Deepak-Kesavan Sep 21, 2026
61985aa
UN-1924 [FIX] Require a PDF version header before rescuing a file
Deepak-Kesavan Sep 21, 2026
1b274b5
UN-1924 [FIX] Resolve zip containers, which libmagic will not name fr…
Deepak-Kesavan Sep 21, 2026
048e94f
UN-1924 [REFACTOR] Move the shared MIME gate logic into unstract/core
Deepak-Kesavan Sep 21, 2026
85d3ae5
UN-1924 [FIX] Bound the ODF mimetype read so a zip bomb cannot expand…
Deepak-Kesavan Sep 21, 2026
6c953a9
UN-1924 [FIX] Stop an archive nominating its own file type
Deepak-Kesavan Sep 21, 2026
3b50d5e
UN-1924 [MISC] Leave one throwing call inside the raises block
Deepak-Kesavan Sep 21, 2026
9c8e9ff
UN-1924 [MISC] Pin the zip-carrying-a-PDF-marker case
Deepak-Kesavan Sep 21, 2026
81a9e6a
UN-1924 [FIX] Three small inconsistencies in the new terminal paths
Deepak-Kesavan Sep 21, 2026
f059e50
UN-1924 [FIX] Count and keep files rejected before dispatch
Deepak-Kesavan Sep 21, 2026
2d70dfd
UN-1924 [FIX] Keep two rejections apart when the bytes are identical
Deepak-Kesavan Sep 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
UN-1924 [FIX] Three small inconsistencies in the new terminal paths
The worker's COMPLETED arm wrote total_files=0 and left the other two
counters NULL — the exact shape update_execution_completed exists to avoid,
since a terminal row with a NULL failed_files reads as a clean success.
Nothing ran, so both are now written as zero.

Its ERROR arm named the reason under "message" while the function's other
two ERROR returns use "error", so a consumer reading .get("error") to learn
why an execution failed got None for this variant alone.

When the all-rejected status write raises, the response carried no error at
all, unlike the sibling staging-failure path — a bare 422 with no reason,
while the row is still PENDING so a follow-up GET /status says something
else. The swallowed exception is now reported.

Also corrects a comment that claimed a field-scoped save() is safe against
concurrent writers: it is, for the columns, but save() re-runs
_handle_execution_cache() regardless of update_fields, which another comment
in the same file already explains. No live bug at this call site — the row is
freshly created and undispatched — but the comment said otherwise.
  • Loading branch information
Deepak-Kesavan committed Sep 21, 2026
commit 81a9e6a951af806f4d156ed4cfdb15bf9f723a5b
8 changes: 7 additions & 1 deletion backend/api_v2/deployment_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,14 +322,19 @@ def execute_workflow(
# Isolate the DB write the way the staging-failure path above does, so
# the rate limit slot and staging dir are released even if it raises.
execution = None
terminalise_error: str | None = None
try:
execution = WorkflowExecutionServiceHelper.update_execution_completed(
str(execution_id),
total_files=len(file_objs),
failed_files=len(file_objs),
)
except Exception:
except Exception as error:
logger.exception(f"Failed to mark execution {execution_id} as COMPLETED")
Comment thread
Deepak-Kesavan marked this conversation as resolved.
# Kept for the response: the sibling staging-failure path reports
# str(error), and without it the caller gets a bare 422 while the
# row is still PENDING, so a follow-up GET /status contradicts it.
terminalise_error = str(error)

APIDeploymentRateLimiter.release_slot(
str(api.organization.organization_id), str(execution_id)
Expand Down Expand Up @@ -377,6 +382,7 @@ def execute_workflow(
execution_status=(
execution.status if execution else ExecutionStatus.ERROR.value
),
error=terminalise_error,
result=api_results,
)
).data
Comment thread
Deepak-Kesavan marked this conversation as resolved.
Expand Down
9 changes: 7 additions & 2 deletions backend/workflow_manager/workflow_v2/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,13 @@ def update_execution_completed(
execution.total_files = total_files
execution.successful_files = 0
execution.failed_files = failed_files
# Field-scoped, matching update_execution, so this cannot clobber the
# status write or anything a concurrent writer touched.
# Field-scoped, so the status column and anything a concurrent writer
# touched are left alone. Note this is scoped to the DB row only:
# WorkflowExecution.save() re-runs _handle_execution_cache() whatever
# update_fields says, republishing this object's in-memory status to
# the Redis cache — see the fuller account in
# update_execution_queue_message_id. Harmless here because the row is
# freshly created and undispatched, so no worker can have moved it on.
execution.save(
update_fields=[
"total_files",
Expand Down
10 changes: 9 additions & 1 deletion workers/api-deployment/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,10 @@ def _unified_api_execution(
return {
"execution_id": execution_id,
"status": "ERROR",
"message": error_message,
# "error", matching this function's other ERROR returns; a
# consumer reading .get("error") to learn why gets None if
# this one names it something else.
"error": error_message,
"files_processed": 0,
}

Expand All @@ -258,6 +261,11 @@ def _unified_api_execution(
execution_id=execution_id,
status=ExecutionStatus.COMPLETED.value,
total_files=0,
Comment thread
Deepak-Kesavan marked this conversation as resolved.
# Written explicitly: a terminal row whose counters are NULL
# reads as a clean success, which is the shape update_execution_
# completed exists to avoid. Nothing ran, so both are zero.
successful_files=0,
failed_files=0,
)
Comment thread
Deepak-Kesavan marked this conversation as resolved.
Comment thread
Deepak-Kesavan marked this conversation as resolved.
if pipeline_id:
api_client.update_pipeline_status(
Expand Down
Loading