ES /docs

AwsTask#fetch! — cross-region IAM permission denied

Validation: f94d4727-d96b-4563-87b9-5d224fec68cd

Verdict: APPROVED#

Completeness#

Plan Item Status Evidence
app/models/aws_task.rb: Add Aws::ECS::Errors::AccessDeniedException rescue to fetch! method wrapping describe_tasks call PASS app/models/aws_task.rb: lines 69-78 in diff show the describe_tasks call (lines 70-74) wrapped in a begin/rescue Aws::ECS::Errors::AccessDeniedException => e block, with error logging and return

Acceptance Criteria#

Criterion Status Evidence
fetch! method has Aws::ECS::Errors::AccessDeniedException rescue clause PASS app/models/aws_task.rb: diff lines +69 (begin), +75 (rescue Aws::ECS::Errors::AccessDeniedException => e), +78 (end) show the rescue clause wrapping the describe_tasks call
Rescue clause logs via Cupix::Logger.error with "AccessDeniedException" message and task_id PASS app/models/aws_task.rb: diff line +76 shows Cupix::Logger.error("AccessDeniedException on task_id: #{task_id}, message: #{e.message}", class: self.class.name, function: __method__, task: { task_id: task_id }) which includes both the exception name and task_id
Code style consistent with stop! method's existing exception handling pattern (lines 114-121) PASS app/models/aws_task.rb: The stop! method (lines 112-127 in the file) uses the pattern rescue Aws::ECS::Errors::XxxException => e followed by Cupix::Logger.error("XxxException on task_id: #{task_id}, message: #{e.message}", class: self.class.name, function: __method__, task: { task_id: task_id }). The new rescue clause at diff lines +75-76 uses this identical pattern: same string interpolation format, same keyword arguments (class:, function:, task:), and same begin/rescue/end block structure

Issues Found#

No blocking issues found.

Observations#

  • The fetch! method returns nil (via bare return) on AccessDeniedException, which propagates to callers: pull! (line 47) will call self.save with unchanged attributes, and fetch (line 104-110) will return true (via the else clause). The pull method (line 51-57) calls pull! which calls fetch! then save, so a denied task will still trigger a save with no attribute changes. This is not a bug per se but is worth noting for operational awareness -- the record will be saved with its existing state.
  • The stop! method (lines 112-139) does not return after its rescue clauses; it falls through to the resp.blank? check on line 132. In contrast, the new fetch! rescue does return immediately. This difference is reasonable because fetch! needs resp to proceed with further processing (lines 80-93), whereas stop! handles the nil-response case via the resp.blank? guard. The approaches are functionally equivalent for their respective methods.