Apollo-client: Catching Network error: Converting circular structure to JSON

Created on 13 Feb 2017  路  13Comments  路  Source: apollographql/apollo-client

Trying hours to debug and trace this error with no luck. Any ideas why apollo is spitting out
"Network error: Converting circular structure to JSON"

The mutation works and the server received the data. and returned expected value. But apollo is still spitting out the JSON error. Anyone have any ideas whats causing this?

Most helpful comment

To anyone else coming here. I ran into this doing

<button onClick={refetch}>Refresh</button>

Ran into the issue because the onClick event object was passed to the refresh method, which tried to serialize it as variables. Clearly not going to work.

Fixed by doing

<button onClick={() => refetch()}>Refresh</button>

All 13 comments

We鈥檙e going to need some more context to help debug this 馃槪

Do you have any stack traces? What does your query look like? What does your mutation look like? Could you put together a small case that reproduces this error?

@calebmer sorry not much info given starting was so stressed out I just had to throw out a ticket to sleep.. Not sure if this is all you need? I'll keep trying, I didnt realize I can remove my catch and actually see the trace bubbled up on dev console.

If I remove the try catch in my code i get
developer_tools_-_http___localhost_3500_lead_leads

And in some cases I get this..
developer_tools_-_http___localhost_3500_client_general_28

code looks like so

const withMutations = graphql(UPDATE_CLIENT_GENERAL, {
  props: ({ mutate }) => ({
    updateUser: ({ ...formState }) => mutate({ variables: { ...formState } }),
  })
});

const withData = graphql(CLIENT_GENERAL, {
  options: props => ({
    variables: {
      id: props.params.id,
    },
    forceFetch: true,
  }),
  props: ({ data: { loading, user } }) => ({
    loading,
    user
  }),
});

export default 
    withMutations(
      withData(
        ClientGeneralPage
      )
);


query($id:Int) { 
  user(id:$id) { 
    id
    accountInformation
    personalInformationClient
    personalInformationSpouse
    currentEmploymentInformationClient
    currentEmploymentInformationSpouse
    generalInformation
    taxInformation
    unemploymentInsurance
    fillingInformation
    family
  }
}
mutation(
  $id: ID!
  $accountInformation: JSON
  $personalInformationClient: JSON
  $personalInformationSpouse: JSON
  $currentEmploymentInformationClient: JSON
  $currentEmploymentInformationSpouse: JSON
  $generalInformation: JSON
  $taxInformation: JSON
  $unemploymentInsurance: JSON
  $fillingInformation: JSON
  $family: [JSON]
) { 

  updateClientGeneral(
    id: $id
    accountInformation: $accountInformation
    personalInformationClient: $personalInformationClient
    personalInformationSpouse: $personalInformationSpouse
    currentEmploymentInformationClient: $currentEmploymentInformationClient
    currentEmploymentInformationSpouse: $currentEmploymentInformationSpouse
    generalInformation: $generalInformation
    taxInformation: $taxInformation
    unemploymentInsurance: $unemploymentInsurance
    fillingInformation: $fillingInformation
    family: $family
  ) { 
    id 
  }

}



md5-0f4d9965fce7d6fe599f227d1df340d4



async submitForm(formState) {
    const { id } = this.props.params;
    try {
      console.log(id, '======');
      // __typename attribute is something apollo tacked on 
      // removing because the server throws error with it being there
      formState.id = parseInt(id)
      await this.props.updateUser(omitDeep(formState, ['__typename']))
      console.log(id, '======');
      this.props.router.push('/client/misc/' + id);
    } catch (error) {
      console.log(JSON.stringify(error));
      if(error.graphQLErrors[0]) {
        alert(error.graphQLErrors[0]);
      }
    }
  }

What does your network interface code look like (with any middleware and afterware)? If you could make a small reproduction using react-apollo-error-template it would go a long way in helping us identify and fix the error.

Code below, I'll work on the getting a reproduction with your template.

import config from '../config'

import ApolloClient, { createNetworkInterface } from 'apollo-client';

const networkInterface = createNetworkInterface({
  uri: config.serverUrl + '/graphql',
});

networkInterface.use([{
  applyMiddleware(req, next) {
    if (!req.options.headers) {
      req.options.headers = new Headers();
    }
    // get the authentication token from local storage if it exists
    const token = localStorage.getItem('token');
    req.options.headers.authorization = token ? `Bearer ${token}` : null;
    next();
  }
}]);

const client = new ApolloClient({
  networkInterface,
  reduxRootSelector: (state) => state.get('apollo'),
});

export default client;

export const apolloReducer = client.reducer();

/**
 * Create the store with asynchronously loaded reducers
 */

import { createStore, applyMiddleware, compose } from 'redux';
import { fromJS } from 'immutable';
import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga';
import createReducer from './reducers';

// Import global sagas
import globalSagas from './sagas';

const sagaMiddleware = createSagaMiddleware();

export default function configureStore(initialState = {}, apolloClient, history) {
  // Create the store with two middlewares
  // 1. sagaMiddleware: Makes redux-sagas work
  // 2. routerMiddleware: Syncs the location/URL path to the state
  const middlewares = [
    sagaMiddleware,
    apolloClient.middleware(),
    routerMiddleware(history),
  ];

  const enhancers = [
    applyMiddleware(...middlewares),
  ];

  // If Redux DevTools Extension is installed use it, otherwise use Redux compose
  /* eslint-disable no-underscore-dangle */
  const composeEnhancers =
    process.env.NODE_ENV !== 'production' &&
    typeof window === 'object' &&
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose;
  /* eslint-enable */

  const store = createStore(
    createReducer(),
    fromJS(initialState),
    composeEnhancers(...enhancers)
  );

  // Run Global Saga automatically
  sagaMiddleware.run(...globalSagas);

  // Extensions
  store.runSaga = sagaMiddleware.run;
  store.asyncReducers = {}; // Async reducer registry

  // Make reducers hot reloadable, see http://mxs.is/googmo
  /* istanbul ignore next */
  if (module.hot) {
    module.hot.accept('./reducers', () => {
      System.import('./reducers').then((reducerModule) => {
        const createReducers = reducerModule.default;
        const nextReducers = createReducers(store.asyncReducers);

        store.replaceReducer(nextReducers);
      });
    });
  }

  return store;
}

Hi @calebmer I'm having a problem with the error template

Failed to compile.

Error in ./src/index.js
Module build failed: TypeError: /Users/andrew/Desktop/react-apollo-error-template/src/index.js: Cannot read property 'scope' of undefined
 @ multi main

Hmm. I don鈥檛 get the same problem with the error template. If you鈥檙e having issues feel free to use whatever tools you are most comfortable with 馃憤

We have seen some issues with this before: req.options.headers = new Headers();. Maybe try req.options.headers = {}; instead?

@calebmer I figured it out. I'm using simple-react-form and mashed it up in such an edge case that it worked even though it shouldn't....

FYI for anyone mashing apollo and simple-react-form I have the HOC container that contains the submission logic and the actual form skeleton as a child. The problem was i wired up the button at the end of the form without the state like below.

<button className="btn btn-primary" onClick={this.props.submitForm}>
   Submit
</button>

In the end I just remove the onClick and do the following.

 <Form state={this.state} onSubmit={() => this.props.submitForm(this.state)} onChange={changes => this.setState(changes)}>

Having the onClick wired to the submit works and apollo breaks trying to save the mutation history. The parameter of the method which should be the formState is a proxyobject/synthetic mouse? Not really sure why it even works in the first place. I think to apollo-client it looks garbled because its a ProxyObject?

PS. react-apollo-error-template works after i update my node to 6

Closing ticket.
Cheers

To anyone else coming here. I ran into this doing

<button onClick={refetch}>Refresh</button>

Ran into the issue because the onClick event object was passed to the refresh method, which tried to serialize it as variables. Clearly not going to work.

Fixed by doing

<button onClick={() => refetch()}>Refresh</button>

@dyst5422 you are amazing... I debugged down through and skipped right over the serialization concern, nice catch!

To anyone else coming here. I ran into this doing

<button onClick={refetch}>Refresh</button>

Ran into the issue because the onClick event object was passed to the refresh method, which tried to serialize it as variables. Clearly not going to work.

Fixed by doing

<button onClick={() => refetch()}>Refresh</button>

You, Sir, are my hero

I got this error because I was doing the following:

  <form
    className={classes.composeForm}
    onSubmit={ e => {
      e.preventDefault();
      if (onProductSave) {
        setProductSaving(true);
        let variables;
        variables = {
          title: e.target.title,
          description: e.target.description,
          category: e.target.category,
        }
        onProductSave({
           variables
        })
        .then(() => {
          setProductSaving(false);
        })
        .catch( e => {
          setProductSaving(false);
        });
   }}>

I was failing to reference .value within the form input fields, so the payload was attempting to serialize the DOM elements (form inputs) themselves/

Fixed with the following:

  <form
    className={classes.composeForm}
    onSubmit={ e => {
      e.preventDefault();
      if (onProductSave) {
        setProductSaving(true);
        let variables;
        variables = {
          title: e.target.title.value,
          description: e.target.description.value,
          category: e.target.category.value,
        }
        onProductSave({
           variables
        })
        .then(() => {
          setProductSaving(false);
        })
        .catch( e => {
          setProductSaving(false);
        });
   }}>

For future readers who read this from the search:
Another reason could be the server is sending back data/error that can't be serialized properly.

Ran into this during testing using apollo-cache-inmemory v2 thanks to this code where it tries to stringify a DocumentNode object which has circular references.

The underlying error for us (exposed by manually throwing it) was a missing __typename in our mocked data.

Was this page helpful?
0 / 5 - 0 ratings