Skip to content
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

Apollo Client throwing a TypeError: options.headers.hasOwnProperty is not a function #11555

Closed
raopg opened this issue Feb 1, 2024 · 29 comments · Fixed by #12054
Closed

Apollo Client throwing a TypeError: options.headers.hasOwnProperty is not a function #11555

raopg opened this issue Feb 1, 2024 · 29 comments · Fixed by #12054

Comments

@raopg
Copy link

raopg commented Feb 1, 2024

Issue Description

This is the stack trace caught by Sentry. We are observing this for a few users while using the Safari 17.2.1.

TypeError: options.headers.hasOwnProperty is not a function. (In 'options.headers.hasOwnProperty(key)', 'options.headers.hasOwnProperty' is undefined)
  at ? (webkit-masked-url://hidden/:418:63)
  at <anonymous>(../../../node_modules/.aspect_rules_js/@[email protected]_1356184083/node_modules/@apollo/client/link/http/createHttpLink.js:127:13)
  at Subscription(../../../node_modules/.aspect_rules_js/[email protected]/node_modules/zen-observable-ts/module.js:190:34)
  at _proto3.subscribe(../../../node_modules/.aspect_rules_js/[email protected]/node_modules/zen-observable-ts/module.js:264:16)
  at <anonymous>(../../../node_modules/.aspect_rules_js/@[email protected]_1356184083/node_modules/@apollo/client/link/error/index.js:11:42)

Link to Reproduction

I've included the stack trace above. We are unable to reproduce this ourselves, but we have Sentry capturing the stack trace with sourcemaps.

Reproduction Steps

No response

@apollo/client version

3.8.8

@jerelmiller
Copy link
Member

jerelmiller commented Feb 1, 2024

Hey @raopg 👋

That is very odd. For one, that line number makes no sense given the code. Here is the code for 3.8.8 in createHttpLink:

You'll see that its just an empty line. The closest thing I could maybe see is this one:

return err.hasOwnProperty("graphQLErrors");

I do see this file in that stack trace, but again, this line number doesn't line up with that stack trace above. That and this change has been here for what looks like 7 years, so I would have assumed we'd see something by now if this was the reason. All of our other usages of hasOwnProperty are pulled from Object.prototype directly.

Is there any more information you're able to give us? Is this specific to 3.8.8 and does downgrading/upgrading to a specific version help in any way?

@jerelmiller jerelmiller added the ℹ needs-more-info Needs more information to determine root cause label Feb 1, 2024
@raopg
Copy link
Author

raopg commented Feb 1, 2024

Hi @jerelmiller,
I think the minification might have changed the line numbers, but here is the code captured by Sentry

            // #7832), or (if all else fails) the backupFetch function we saved when
            // this module was first evaluated. This last option protects against the
            // removal of window.fetch, which is unlikely but not impossible.
            var currentFetch = preferredFetch || maybe(function () { return fetch; }) || backupFetch;
            var observerNext = observer.next.bind(observer);
            currentFetch(chosenURI, options)
                .then(function (response) {
                var _a;
                operation.setContext({ response: response });
                var ctype = (_a = response.headers) === null || _a === void 0 ? void 0 : _a.get("content-type");
                if (ctype !== null && /^multipart\/mixed/i.test(ctype)) {

Screenshot 2024-02-01 at 1 00 17 PM

Here is a screenshot, so you can see the line numbers. It is a very odd case, since our engineering team is unable to reproduce it in a dev or prod environment. 2 of our customers are seeing this only on Safari (with all browser extensions disabled).

@raopg
Copy link
Author

raopg commented Feb 1, 2024

We have been seeing this since Apollo 3.7.3, and upgrading/downgrading versions of Apollo has not helped

@jerelmiller
Copy link
Member

Awesome. Appreciate that context! Let me go and see what might have changed in that version to try and get a clue. Gotta love those cross browser quirks sometimes (if thats what it is 😆)

@jerelmiller
Copy link
Member

Interestingly I don't see anything changed having to do with headers in between 3.7.2 and 3.7.3. Here's a different view of it that shows the diff between the compiled versions. Looking for changes to headers doesn't yield any changes to anything with hasOwnProperty, so I'm a bit at a loss.

Unfortunately I've got a few other priorities I've got to look at today, so I'll need to revisit later. If you have a revelation or spot it before I do, let me know and we'd be happy to address! PRs welcome as well 🙂

@jerelmiller jerelmiller added 🔍 investigate Investigate further 🏓 awaiting-team-response requires input from the apollo team and removed ℹ needs-more-info Needs more information to determine root cause labels Feb 1, 2024
@jerelmiller
Copy link
Member

Adding a note here, this might be possibly related: #10403

@jerelmiller
Copy link
Member

@raopg what does your link chain look like? Would you be able to provide what your ApolloClient initialization looks like (omitting any sensitive info if present)? #10403 looks very similar and I'm wondering if there is something buried inside the link chain as discussed in that issue.

@jerelmiller
Copy link
Member

jerelmiller commented Feb 1, 2024

@raopg see this comment in #10403 to see if this applies in your situation at all.

@raopg
Copy link
Author

raopg commented Feb 2, 2024

    const graphQLEndpoint = "/api/graphql";
    const uri = new HttpLink({ uri: graphQLEndpoint });
    const errorHandler = onError(({ networkError, operation }) => {/* error logging */});

    const requestContextMiddleware = new ApolloLink((operation, forward) => {
      operation.setContext(({ headers = {} }) => ({
        headers: {
          ...headers,
          "Request-ID": uuidv4(),
          ...(appContext.url_name
            ? { "Account-Uri": appContext.url_name }
            : {}),
        },
      }));

      return forward(operation);
    });
    
        return new ApolloClient({
      link: errorHandler.concat(requestContextMiddleware).concat(uri),
      cache,
    });

This is our link setup

@raopg
Copy link
Author

raopg commented Feb 2, 2024

From what I gather in that comment, it doesnt seem like its the same issue. I will also investigate to see if there is anything I can spot

@jerelmiller
Copy link
Member

Bummer, worth a shot. Do let me know if you come up with anything!

The only other thing I can think of is perhaps to check the bundled output of the client and see if you spot anything in there with options.headers.hasOwnProperty? Perhaps the bundler is compiling the output of some expression into a hasOwnProperty call?

This is the only thing I can think of at this point 😕

@jerelmiller jerelmiller added 🥀 needs-reproduction and removed 🏓 awaiting-team-response requires input from the apollo team labels Feb 5, 2024
@radu-nicoara-bayer
Copy link

We are getting this from the preflight call, but I was unable to reproduce it in a code sandbox.
If we add "Access-Control-Max-Age": 600 on the server side, it works for 10 minutes, until the next preflight

@radu-nicoara-bayer
Copy link

radu-nicoara-bayer commented Jul 3, 2024

You can test the issue here, but only appears on Safari Breowser:

Sandbox

The error appears in Safari, when the preflight happens. Just open in a new window, and it will be there
image

@jerelmiller
Copy link
Member

jerelmiller commented Jul 3, 2024

Hey @radu-nicoara-bayer 👋

Thanks for providing that reproduction! Unfortunately I'm still not able to reproduce the issue, even in Safari. Our best lead right now is that this might come from some kind of Safari extension. What Safari version are you using and what extensions do you have installed? I'd like to install them myself to understand if one of them might be at play here.

We had an internal report of something very similar as well. We noticed the stack trace of the error included something like the following:

(webkit-masked-url://hidden/:418:63)

(which also corresponds with the initial report here)

From what we can find, this looks like something Safari does with extensions: https://bugs.webkit.org/show_bug.cgi?id=246010. Part of the stack trace also seems to point at the currentFetch call itself:

currentFetch!(chosenURI, options)

My guess is that an extension is trying to replace window.fetch with some wrapped version to be able to analyze the calls, but its got a bug that doesn't handle headers properly. Any more information you can provide would be super helpful!

@radu-nicoara-bayer
Copy link

My safari version is Version 17.5 (19618.2.12.11.6), and it is running on a Mac M1 (Maybe that matters as well). As for extensions, I have nothing installed.
The user that reported this also has the default Safari on a Mac Book M1.

@phryneas
Copy link
Member

phryneas commented Jul 4, 2024

My safari version is Version 17.5 (19618.2.12.11.6), and it is running on a Mac M1

That matches with the setup I have here - unfortunately I don't see the error in the CodeSandbox :/

As for extensions, I have nothing installed.

Are you sure there's not maybe some plugin or extension installed by something like 1Password?
I have to admit, apart from the "extensions" route, right now we're pretty much out of ideas - and the StackTrace is pointing to some "internals" that are hidden from page JS, so an extension would be the most obvious idea.

@radu-nicoara-bayer
Copy link

You are correct. There is a security extension installed.
image
I opened an incognito, and the error is no longer there.

@phryneas
Copy link
Member

phryneas commented Jul 5, 2024

What annoyingly hidden bug - I'm glad you could find that!

I guess at this point, we can close the issue here and you make a bug report with the vendor of that extension? (If it ends up in a public bug tracker, I'd would be very thankful if you could link that here!)

@dotku
Copy link

dotku commented Aug 25, 2024

It seems all iOS has the issue :( social.dotku.us

@phryneas
Copy link
Member

@dotku Could you please provide your iOS version, browser version and a list of all installed browser extensions or other installed "privacy"-type apps?

@spnc-omz
Copy link

spnc-omz commented Sep 3, 2024

I ran into this issue myself where the Forcepoint Safari Extension / Websense Endpoint Helper extension was forced on to my computer by corporate leaving me with no options but to dive in and see what is going on.

Inside of the SafariExtension, they hijack the window.fetch and XMLHttpRequest and add what they need on top of them to monitor all requests. In this they have a block of code that checks options.headers and does some sort of reassignment for their monitoring use cases. In this, they expect the headers object to be a full object prototype with hasOwnProperty function but for some reason, only in Safari, only when doing a spread of the headers, this function doesn't exist.

The right fix is for the plugin to do a, Object.prototype.hasOwnProperty.call(object, key) because that uses the Object.prototype instead of hoping the thing being passed in has the prototype functions it wants. Eslint has been encouraging this behavior for ages now. https://eslint.org/docs/latest/rules/no-prototype-builtins . Sadly we can't change the vendor's code so I needed to find a workaround.

What I ended up doing to get things to work, and mind you I'm not using this apollo client directly, I'm using @nuxtjs/apollo to handle a lot of the heavy lifting, but the parameters existed for me to get to what I needed.

Apollo Client exposes custom fetching https://www.apollographql.com/docs/react/networking/advanced-http-networking/#custom-fetching , see also

const currentFetch = preferredFetch || maybe(() => fetch) || backupFetch;
. In the plugin I'm using it's part of the httpLinkOptions, I was able to pass it the following fetch to make things happy.

httpLinkOptions: {
    fetch: async (uri, options) => {
      Object.assign(options, {
        headers: {
            "x-my-headers": "yes"
        }
      });
      
      return await fetch(uri, options);
    },
}

For whatever reason, the object assign of the headers object got me around the error.

I hope this helps anyone else who stumbles upon this error. This is absolutely feels like a gross work around for someone else's bug. If anyone has a way to submit a bug to forcepoint, please do.

@dotku
Copy link

dotku commented Sep 3, 2024

@dotku Could you please provide your iOS version, browser version and a list of all installed browser extensions or other installed "privacy"-type apps?

I have no idea, but somehow it is fixed automatically after redeploy?!

@phryneas
Copy link
Member

phryneas commented Sep 4, 2024

@spnc-omz that puts enough puzzle pieces in place that we might also be able to work around it on our side.

Could you please give npm i @apollo/[email protected] a try and report back if it also solves the issue?

@spnc-omz
Copy link

spnc-omz commented Sep 4, 2024

@phryneas I was able to give this a look in my project in safari and I'm no longer seeing the issue. I did have a few hiccups getting there because of the layers of abstraction provided by frameworks, but was able to verify.

Thanks for getting around to this so quickly! y'all are awesome.

@phryneas
Copy link
Member

phryneas commented Sep 5, 2024

@spnc-omz thank you for the feedback - I'm getting this released then :)

Copy link
Contributor

github-actions bot commented Sep 5, 2024

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.

@phryneas
Copy link
Member

phryneas commented Sep 5, 2024

And it's out with https://github.com/apollographql/apollo-client/releases/tag/v3.11.8

@danielbucher
Copy link

Thanks for that @spnc-omz and @phryneas.

Copy link
Contributor

github-actions bot commented Oct 6, 2024

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
For general questions, we recommend using StackOverflow or our discord server.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 6, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants