Skip to content

fix: stop double-reporting errors via Log::error in handleException#4

Merged
JasonHolderness-bl merged 1 commit into
masterfrom
fix/no-duplicate-error-logging
May 11, 2026
Merged

fix: stop double-reporting errors via Log::error in handleException#4
JasonHolderness-bl merged 1 commit into
masterfrom
fix/no-duplicate-error-logging

Conversation

@JasonHolderness-bl

Copy link
Copy Markdown

Summary

Removes the Log::error(..., ['exception' => \$e]) call inside LeverClient::handleException(). The thrown LeverApiException already carries statusCode, responseBody, and the original Guzzle exception as \$previous — everything a caller needs to log or classify the error. Logging is now the caller's responsibility.

The wrapped exception message also now includes the endpoint ("HTTP $method $endpoint failed: ...") so callers don't need to inspect the previous chain to identify the failing call.

Why this matters

Downstream Laravel apps using Bugsnag (or any monolog handler that promotes the exception context to a separate error) were getting two reports per failed Lever request — once from this internal Log::error (grouped as GuzzleHttp\Exception\ClientException) and again when the caller's queue worker / exception reporter handled the propagated LeverApiException.

In one observed production case in the Spot project, this produced 120 + 104 = 224 redundant Bugsnag events for the same handful of permanently-failing job sync attempts.

Notes

  • Backwards compatible at the type level: LeverApiException extends RuntimeException, so any caller using catch (RuntimeException $e) is unaffected.
  • The exception message changed from "Error executing HTTP $method. Please check the logs for more details." to "HTTP $method $endpoint failed: <original message>". Callers matching on message text would need to update — none in the test suite do.
  • Removed the now-unused use Illuminate\Support\Facades\Log; import.

Test plan

  • Existing tests pass (OpportunitiesTest::fail_to_create_opportunity_when_no_perform_as_parameter_included still passes via RuntimeException::class parent match)
  • OpportunitiesTest::client_error_exposes_status_and_response_body still validates statusCode/responseBody/previous chain
  • Tag v1.0.11 after merge

🤖 Generated with Claude Code

The Log::error call in handleException caused duplicate error reports in
consumers using Bugsnag's Laravel log handler: once from this internal
log call (as the original Guzzle ClientException) and again when the
caller's queue worker / exception reporter handled the thrown
LeverApiException.

The thrown LeverApiException already carries statusCode, responseBody,
and the original exception as $previous, so callers have everything
they need to log or classify the error. Logging is now the caller's
responsibility.

The wrapped exception message now includes the endpoint for easier
debugging without requiring callers to inspect the previous chain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JasonHolderness-bl
JasonHolderness-bl merged commit 54d2899 into master May 11, 2026
3 checks passed
@JasonHolderness-bl
JasonHolderness-bl deleted the fix/no-duplicate-error-logging branch May 20, 2026 00:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant