Skip to content

Feedback widget with screenshot via tunnelling not working #16112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
3 tasks done
anatoliy-t7 opened this issue Apr 23, 2025 · 31 comments
Closed
3 tasks done

Feedback widget with screenshot via tunnelling not working #16112

anatoliy-t7 opened this issue Apr 23, 2025 · 31 comments

Comments

@anatoliy-t7
Copy link

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/sveltekit

SDK Version

9.13.0

Framework Version

2.20.7

Link to Sentry event

No response

Reproduction Example/SDK Setup

src/hooks.client.ts

Sentry.init({
  dsn: env.PUBLIC_SENTRY_DSN,
  tunnel: `${env.PUBLIC_URL}/sentry`,
  environment: env.PUBLIC_ENV,
  release: version.sha,
  tracesSampleRate: 1,
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
  enabled: isSentryEnabled,
  integrations: isSentryEnabled
    ? [
        replayIntegration(),
        feedbackIntegration({
          colorScheme: 'system',
          showBranding: false,
          themeLight: {
            accentBackground: '#1D74E3'
          },
          themeDark: {
            accentBackground: '#1D74E3',
            background: '#1c1b1d'
          }
        })
      ]
    : [],
  debug: false
});

src/routes/(public)/sentry/+server.ts

export const POST: RequestHandler = async ({ request }) => {
  const envelopeBytes = await request.arrayBuffer();
  const envelope = new TextDecoder().decode(envelopeBytes);

  try {
    const piece = envelope.split('\n')[0];
    const header = JSON.parse(piece);
    const dsn = new URL(header['dsn']);
    const project_id = dsn.pathname?.replace('/', '');

    if (dsn.hostname !== SENTRY_HOST) {
      throw new Error(`Invalid sentry hostname: ${dsn.hostname}`);
    }

    if (!project_id || !SENTRY_PROJECT_IDS.includes(project_id)) {
      throw new Error(`invalid sentry project id: ${project_id}`);
    }

    const upstream_sentry_url = `https://${SENTRY_HOST}/api/${project_id}/envelope/`;

    await fetch(upstream_sentry_url, {
      method: 'POST',
      body: envelopeBytes
    });

    return json({ status: 200 });
  } catch (e) {
    logger.error('error tunneling to sentry', e);

    return json({ error: 'error tunneling to sentry' }, { status: 500 });
  }
};

Steps to Reproduce

  1. set up feedback widget https://docs.sentry.io/platforms/javascript/guides/sveltekit/user-feedback/
  2. choose to take screenshot via a feedback widget
  3. add a message
  4. send feedback

Expected Result

A feedback should reach sentry with a screenshot attached

Actual Result

  • a feedback did not reach Sentry
  • get a message Unable to send Feedback. This could be because of network issues, or because you are using an ad-blocker
  • a tunnel backend endpoint gets error from sentry response -> SyntaxError Unexpected end of JSON input

Sentry gets a feedback without a screenshot, but a tunnel backend endpoint gets error from sentry response too SyntaxError Unexpected end of JSON input

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Apr 23, 2025
@anatoliy-t7 anatoliy-t7 changed the title Feedback widget with screenshot via tunneling not working Feedback widget with screenshot via tunnelling not working Apr 23, 2025
Copy link
Member

mydea commented Apr 23, 2025

hey, I guess the tunnel is not handling attachments properly 🤔 cc @ryan953 or @AbhiPrasad any idea how the tunnel has to be adjusted so that tunneling an attachment would work?

@AbhiPrasad
Copy link
Member

attachments should work afaik, we shouldn't do anything with the envelope. @timfish do any problems here ring a bell for you?

@timfish
Copy link
Collaborator

timfish commented Apr 23, 2025

I think the issue is that you can't safely convert entire envelopes containing non-text attachments to a string like this:

  const envelopeBytes = await request.arrayBuffer();
  const envelope = new TextDecoder().decode(envelopeBytes);

You need to slice the header from the envelope at the first newline in bytes before converting in to text:

export const POST: RequestHandler = async ({ request }) => {
  try {
    const envelopeBytes = new Uint8Array(await req.arrayBuffer());
    const headerEnd = envelopeBytes.indexOf(0x0a); // Find the first newline
    const headerBytes = envelopeBytes.subarray(0, headerEnd);
    const header = JSON.parse(new TextDecoder().decode(headerBytes));
    const dsn = new URL(header['dsn']);
    const project_id = dsn.pathname?.replace('/', '');

    if (dsn.hostname !== SENTRY_HOST) {
      throw new Error(`Invalid sentry hostname: ${dsn.hostname}`);
    }

@timfish
Copy link
Collaborator

timfish commented Apr 23, 2025

I'll test the code changes above and make the appropriate changes to the docs where that code sample comes from:
https://docs.sentry.io/platforms/javascript/guides/nextjs/troubleshooting/#using-the-tunnel-option

@anatoliy-t7
Copy link
Author

@timfish thank you for helping, but I still getting same error after changed the code

export const POST: RequestHandler = async ({ request }) => {
  try {
    const envelopeBytes = new Uint8Array(await request.arrayBuffer());
    const headerEnd = envelopeBytes.indexOf(0x0a); // Find the first newline
    const headerBytes = envelopeBytes.subarray(0, headerEnd);
    const header = JSON.parse(new TextDecoder().decode(headerBytes));
    const dsn = new URL(header['dsn']);
    const project_id = dsn.pathname?.replace('/', '');

    if (dsn.hostname !== SENTRY_HOST) {
      throw new Error(`Invalid sentry hostname: ${dsn.hostname}`);
    }

    if (!project_id || !SENTRY_PROJECT_IDS.includes(project_id)) {
      throw new Error(`invalid sentry project id: ${project_id}`);
    }

    const upstream_sentry_url = `https://${SENTRY_HOST}/api/${project_id}/envelope/`;

    await fetch(upstream_sentry_url, {
      method: 'POST',
      body: envelopeBytes
    });

    return json({ status: 200 });
  } catch (e) {
    logger.error('error tunneling to sentry', e);

    return json({ error: 'error tunneling to sentry' }, { status: 500 });
  }
};
ERROR: error tunneling to sentry:
SyntaxError: Unexpected end of JSON input

@timfish
Copy link
Collaborator

timfish commented Apr 24, 2025

Looks like I need to do more testing!

@timfish
Copy link
Collaborator

timfish commented Apr 24, 2025

@anatoliy-t7 can you share a project that reproduces this error or log the result of new TextDecoder().decode(headerBytes) so I can see why it cannot be converted to JSON?

@anatoliy-t7
Copy link
Author

@timfish
Looks like when I send it with a screenshot, it does not go through the tunnel

Without a screenshot:

TUNNEL LOG of new TextDecoder().decode(headerBytes) ->  {"event_id":"9c3b730f7fc749f492678f137cc781a9","sent_at":"2025-04-24T10:26:29.807Z","sdk":{"name":"sentry.javascript.sveltekit","version":"9.13.0"},"dsn":"hidden","trace":{"environment":"dev","release":"dev","public_key":"hidden","trace_id":"1003740a940c7217cbadb09f8da0a1a4","transaction":"GET /(protected)/surveys","sampled":"true","sample_rand":"0.6976181215617028","sample_rate":"1"}}

2025-04-24 10:26:34.877 ERROR   /src/hooks.server.ts:59 dev     handle error {
  status: 500,
  message: 'Internal Error',
  error: SyntaxError: Unexpected end of JSON input

With a screenshot:

2025-04-24 10:28:56.447 ERROR   /src/hooks.server.ts:59 dev     handle error {
  status: 500,
  message: 'Internal Error',
  error: SyntaxError: Unexpected end of JSON input

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Apr 24, 2025
@tcurdt
Copy link

tcurdt commented Apr 24, 2025

@anatoliy-t7 can you please also share the content of new TextDecoder().decode(headerBytes) for both cases?

@anatoliy-t7
Copy link
Author

@tcurdt log on /sentry does not catch anything when I send feedback with a screenshot, this is why I wrote it does not go through the tunnel

@timfish
Copy link
Collaborator

timfish commented Apr 24, 2025

No idea what's going on here, doesn't make much sense on the surface.

If you can supply a reproduction I can take a look and debug it!

@tcurdt
Copy link

tcurdt commented Apr 24, 2025

@timfish since it sounds like the frontend code is not even reaching the tunnel - maybe you could just have a look on our test environment?

https://test.edkimo.com/

If that's an option we can enable the feedback widget.
And if that does not help we need to see how we can create a test case.

@tcurdt
Copy link

tcurdt commented Apr 28, 2025

We could make the browser transport include a content-type when sending via the tunnel. We can't include this by default because it can impact CORS.

Sounds like a plan.
Here is the SvelteKit issue btw:

sveltejs/kit#11842

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Apr 28, 2025
@timfish
Copy link
Collaborator

timfish commented Apr 28, 2025

It's also worth noting that in my testing it looks like the tunnel endpoint gets instrumented by Sentry. If transactions are being captured for these incoming requests you're going to have a server transaction for every frontend envelope sent!

@tcurdt
Copy link

tcurdt commented Apr 28, 2025

It's also worth noting that in my testing it looks like the tunnel endpoint gets instrumented by Sentry. If transactions are being captured for these incoming requests you're going to have a server transaction for every frontend envelope sent!

You mean the transaction itself will also show up in sentry?
IIRC there is some URL filtering to avoid that, right?

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Apr 28, 2025
@timfish
Copy link
Collaborator

timfish commented Apr 28, 2025

Sentry automatically instruments the Node http server, so every POST to /sentry captures a transaction:

Image

@tcurdt
Copy link

tcurdt commented Apr 28, 2025

Right ... but wasn't there a way to remove some of those?
I remember reading about that somewhere?
What about:

tracePropagationTargets
shouldCreateSpanForRequest

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Apr 28, 2025
@timfish
Copy link
Collaborator

timfish commented Apr 28, 2025

but wasn't there a way to remove some of those?

Yes, you can manually filter these out via ignoreTransactions in the server Sentry.init.

We are planning some improvements to the tunnel option, including making it simpler and more streamlined to setup. Filtering out these transactions automatically can go on that list!

@tcurdt
Copy link

tcurdt commented Apr 28, 2025

Ok, you can actually work around this already by adding the headers to the transportOptions:

First tests show this to be working. I'll wait for some more testing.
But I am carefully optimistic this resolves this issue for for us for now.

Thanks for your help!

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Apr 28, 2025
@getsantry getsantry bot moved this from Waiting for: Product Owner to Waiting for: Community in GitHub Issues with 👀 3 Apr 29, 2025
@Lms24
Copy link
Member

Lms24 commented Apr 29, 2025

Thanks for the response! I added the Waiting for: Community label which will auto-close this issue in a few weeks if there are no more replies. Feel free to let us know if it's working and/or close the issue yourself. In case there are still problems, just let us know, too :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests

9 participants