You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found while verifying #324 with a deliberately failing delete. Reproduction is cheap and exact (recipe at the bottom).
What happens
A DynamoDB resource-based policy denying BatchWriteItem for one partition key, then bulk delete over 60 items:
2026-08-28 22:20:35 ERROR TaskSetManager:267 - Task 161 in stage 4.0 failed 4 times; aborting job
exit=1
items remaining: 6 <- 54 of 60 were deleted
From the executor stream:
botocore.exceptions.ClientError: An error occurred (AccessDeniedException) when calling the
BatchWriteItem operation: User: arn:aws:sts::...:assumed-role/AWSGlueServiceRoleBulkDynamoDB-
DdbReadWrite-us-east-1/GlueJobRunnerSession is not authorized to perform ...
Three problems:
The user gets a Spark traceback, not an explanation. Nothing says "you are not allowed to delete from this table". The AccessDeniedException is in an executor stream, which the client discards (_pretty_print_log_event skips _g- streams), so the only thing on the console is Task 161 in stage 4.0 failed 4 times.
The partial delete is silent. 54 items were deleted and 6 were not. The driver's Deleted N items line never prints, because the job aborted first, so nothing states what did or did not happen. A user re-running blind is the best case; a user assuming nothing happened is the likely one.
find's per-item failure reporting cannot see it.batch_writer buffers 25 items and flushes, so the error is raised inside the with block's exit — outside the per-item try. Verified: zero Delete failed for lines were logged, and the M failed count added in [bulk] Bound per-item failure logging, and tell the user the total (closes #319) #324 stayed at zero. That count only covers malformed-record and key-extraction failures, which are rare; it does not cover the failure users will actually hit.
Why it is awkward to fix
A worker-side BulkExecutorError does not reach the driver as a BulkExecutorError — it arrives as a Py4JJavaError wrapping the worker traceback, so root.py's clean-error handler does not catch it, and the client's BulkExecutorError noise suppression never triggers either. Getting a clean message onto the console needs the driver to inspect the wrapped cause, not just a raise in the worker.
Options, roughly in increasing order of work:
Catch at the partition boundary and re-raise with a recognisable shape, then teach the driver (or root.py) to unwrap a Py4JJavaError whose cause carries our marker and print the underlying message. Fixes problem 1.
Report what was done before failing. Read the accumulator on the driver in a finally so Deleted N items, M not attempted prints even on an aborted run. Fixes problem 2.
Decide the policy for a wholesale write failure: abort (today) versus complete-and-report. Aborting is defensible for a permission error, but it must say so; completing with an accurate count risks a successful-looking exit 0 on a run that deleted nothing.
Then bulk delete --table <T> (PITR must be enabled first — the existing guard fires before any of this). Note the policy denies BatchWriteItem for a request containing the key, so one denied item poisons its whole 25-item batch, which is why 6 items rather than 1 survived: the failing task was retried 4 times and the job aborted while other tasks were still in flight.
Clean up with delete_resource_policy, then wait — DeleteTable returns ResourceInUseException until the policy update settles.
Found while verifying #324 with a deliberately failing delete. Reproduction is cheap and exact (recipe at the bottom).
What happens
A DynamoDB resource-based policy denying
BatchWriteItemfor one partition key, thenbulk deleteover 60 items:From the executor stream:
Three problems:
AccessDeniedExceptionis in an executor stream, which the client discards (_pretty_print_log_eventskips_g-streams), so the only thing on the console isTask 161 in stage 4.0 failed 4 times.Deleted N itemsline never prints, because the job aborted first, so nothing states what did or did not happen. A user re-running blind is the best case; a user assuming nothing happened is the likely one.find's per-item failure reporting cannot see it.batch_writerbuffers 25 items and flushes, so the error is raised inside thewithblock's exit — outside the per-itemtry. Verified: zeroDelete failed forlines were logged, and theM failedcount added in [bulk] Bound per-item failure logging, and tell the user the total (closes #319) #324 stayed at zero. That count only covers malformed-record and key-extraction failures, which are rare; it does not cover the failure users will actually hit.Why it is awkward to fix
A worker-side
BulkExecutorErrordoes not reach the driver as aBulkExecutorError— it arrives as aPy4JJavaErrorwrapping the worker traceback, soroot.py's clean-error handler does not catch it, and the client'sBulkExecutorErrornoise suppression never triggers either. Getting a clean message onto the console needs the driver to inspect the wrapped cause, not just araisein the worker.Options, roughly in increasing order of work:
root.py) to unwrap aPy4JJavaErrorwhose cause carries our marker and print the underlying message. Fixes problem 1.finallysoDeleted N items, M not attemptedprints even on an aborted run. Fixes problem 2.Reproduction
Then
bulk delete --table <T>(PITR must be enabled first — the existing guard fires before any of this). Note the policy deniesBatchWriteItemfor a request containing the key, so one denied item poisons its whole 25-item batch, which is why 6 items rather than 1 survived: the failing task was retried 4 times and the job aborted while other tasks were still in flight.Clean up with
delete_resource_policy, then wait —DeleteTablereturnsResourceInUseExceptionuntil the policy update settles.