Skip to content

fix: Do not capture 404 responses from external APIs at Sentry#904

Merged
Woody4618 merged 4 commits intosolana-foundation:masterfrom
hoodieshq:fix/silence-external-api-404-sentry
Mar 26, 2026
Merged

fix: Do not capture 404 responses from external APIs at Sentry#904
Woody4618 merged 4 commits intosolana-foundation:masterfrom
hoodieshq:fix/silence-external-api-404-sentry

Conversation

@askov
Copy link
Copy Markdown
Contributor

@askov askov commented Mar 25, 2026

Description

External APIs (Jupiter, CoinGecko, Rugcheck) return 404 for tokens or coins that simply don't exist — a normal scenario, not an error. Downgrade 404s to Logger.debug so they no longer trigger Sentry alerts, matching the existing pattern in the OSEC verified-programs route.

Type of change

  • Bug fix

Testing

  • Tests should pass

Related Issues

Closes HOO-363

Checklist

  • My code follows the project's style guidelines
  • I have added tests that prove my fix/feature works
  • All tests pass locally and in CI
  • CI/CD checks pass

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 25, 2026

@askov is attempting to deploy a commit to the Solana Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 25, 2026

Greptile Summary

This PR downgrages 404 responses from external APIs (Jupiter price, Jupiter verification, CoinGecko, Rugcheck) from error/panic-level logging with Sentry capture to silent Logger.debug calls, following the existing convention used in the OSEC verified-programs route. A 404 from these APIs simply means the token or coin doesn't exist — a completely normal, expected scenario that should not generate Sentry noise.

  • All four routes (receipt/price, verification/jupiter, verification/coingecko, verification/rugcheck) receive the same consistent fix: a 404 branch is inserted before the existing 429 branch, emitting a debug-level log with no Sentry flag.
  • The rest of the error handling (429 rate limiting → warn, other codes → error/panic) is unchanged.
  • The HTTP response returned to the calling client is unaffected by the logging change: verification routes pass response.status through, and the price route continues to map non-429 errors to 502.
  • No functional regressions are expected; the change is purely observability-scoped.

Confidence Score: 5/5

  • Safe to merge — the change is logging-only and cannot affect runtime behaviour for clients.
  • The PR makes a minimal, well-scoped change: it inserts a 404 branch before the existing 429 branch in four route handlers, switching from error/Sentry logging to a silent debug log. The HTTP responses returned to callers are unchanged. The pattern follows an established convention already in the codebase, and there are no side effects on authentication, data integrity, or API contracts.
  • No files require special attention.

Important Files Changed

Filename Overview
app/api/receipt/price/[mintAddress]/route.ts Adds a 404 branch that logs at debug level (no Sentry) before the existing 429/error branches. On 404, the response still returns 502 to the client (pre-existing status mapping: response.status === 429 ? 429 : 502), but the logging change itself is correct.
app/api/verification/coingecko/[coinId]/route.ts Adds a 404 branch with debug-level logging (no Sentry). The 404 status is forwarded directly to the client via status: response.status, consistent with the other verification routes.
app/api/verification/jupiter/[mintAddress]/route.ts Adds a 404 branch with debug-level logging (no Sentry). 404 is forwarded to the client, consistent with the coingecko and rugcheck verification routes.
app/api/verification/rugcheck/[mintAddress]/route.ts Adds a 404 branch with debug-level logging (no Sentry). 404 is forwarded to the client, consistent with the other verification routes.

Sequence Diagram

sequenceDiagram
    participant Client
    participant RouteHandler
    participant ExternalAPI as External API (Jupiter/CoinGecko/Rugcheck)
    participant Logger
    participant Sentry

    Client->>RouteHandler: GET /api/...
    RouteHandler->>ExternalAPI: fetch(url, headers)

    alt HTTP 404 (token/coin not found)
        ExternalAPI-->>RouteHandler: 404 Not Found
        RouteHandler->>Logger: Logger.debug("... not found") [no Sentry]
        RouteHandler-->>Client: Error response (404 or 502)
    else HTTP 429 (rate limit)
        ExternalAPI-->>RouteHandler: 429 Too Many Requests
        RouteHandler->>Logger: Logger.warn(..., {sentry: true})
        Logger->>Sentry: capture warning
        RouteHandler-->>Client: 429
    else Other HTTP error
        ExternalAPI-->>RouteHandler: 5xx / other
        RouteHandler->>Logger: Logger.error/panic(..., {sentry: true})
        Logger->>Sentry: capture error
        RouteHandler-->>Client: 502
    else HTTP 200 OK
        ExternalAPI-->>RouteHandler: 200 + JSON body
        RouteHandler-->>Client: 200 + data
    end
Loading

Reviews (1): Last reviewed commit: "fix: Do not capture 404 responses from e..." | Re-trigger Greptile

}

if (!RUGCHECK_API_KEY) {
const apiKey = process.env.RUGCHECK_API_KEY;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the previous version, as other tokens are provided this way

Copy link
Copy Markdown
Contributor Author

@askov askov Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you can't mock it in tests with the previous version (actually you can, but it makes the tests harder to read and write, and it slows them down a bit). That's not a problem for others, because they don't have any tests.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, the previous one was RUGCHECK_API_KEY = process.env.RUGCHECK_API_KEY. I agree with the fact that we have to move it inside the function. I mean that literal in CAPS is visible like a constant. apiKey is not


if (!response.ok) {
if (response.status === 429) {
if (response.status === 400 || response.status === 404) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's handle only 404 here. It is good to see 400s, as maybe we might have errors with passing mints to the endpoint

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a lot of spam here due to the 400 status. They return an incorrect status for the error. Perhaps it would be better to add body message parsing? This would help differentiate between a 'true' 400 error and a 404 masked as a 400 with a special body.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a body check to differentiate between 400 status errors

Copy link
Copy Markdown
Contributor

@rogaldh rogaldh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for these LGTM

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
explorer Ready Ready Preview, Comment Mar 26, 2026 0:31am

Request Review

@Woody4618 Woody4618 merged commit c29ffcf into solana-foundation:master Mar 26, 2026
6 of 7 checks passed
@rogaldh rogaldh deleted the fix/silence-external-api-404-sentry branch March 26, 2026 18:08
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.

5 participants