"SyntaxError: Unexpected token < in JSON at position 0"

Cameron Sima picture Cameron Sima · May 17, 2016 · Viewed 856k times · Source

In a React app component which handles Facebook-like content feeds, I am running into an error:

Feed.js:94 undefined "parsererror" "SyntaxError: Unexpected token < in JSON at position 0

I ran into a similar error which turned out to be a typo in the HTML within the render function, but that doesn't seem to be the case here.

More confusingly, I rolled the code back to an earlier, known-working version and I'm still getting the error.

Feed.js:

import React from 'react';

var ThreadForm = React.createClass({
  getInitialState: function () {
    return {author: '', 
            text: '', 
            included: '',
            victim: ''
            }
  },
  handleAuthorChange: function (e) {
    this.setState({author: e.target.value})
  },
  handleTextChange: function (e) {
    this.setState({text: e.target.value})
  },
  handleIncludedChange: function (e) {
    this.setState({included: e.target.value})
  },
  handleVictimChange: function (e) {
    this.setState({victim: e.target.value})
  },
  handleSubmit: function (e) {
    e.preventDefault()
    var author = this.state.author.trim()
    var text = this.state.text.trim()
    var included = this.state.included.trim()
    var victim = this.state.victim.trim()
    if (!text || !author || !included || !victim) {
      return
    }
    this.props.onThreadSubmit({author: author, 
                                text: text, 
                                included: included,
                                victim: victim
                              })
    this.setState({author: '', 
                  text: '', 
                  included: '',
                  victim: ''
                  })
  },
  render: function () {
    return (
    <form className="threadForm" onSubmit={this.handleSubmit}>
      <input
        type="text"
        placeholder="Your name"
        value={this.state.author}
        onChange={this.handleAuthorChange} />
      <input
        type="text"
        placeholder="Say something..."
        value={this.state.text}
        onChange={this.handleTextChange} />
      <input
        type="text"
        placeholder="Name your victim"
        value={this.state.victim}
        onChange={this.handleVictimChange} />
      <input
        type="text"
        placeholder="Who can see?"
        value={this.state.included}
        onChange={this.handleIncludedChange} />
      <input type="submit" value="Post" />
    </form>
    )
  }
})

var ThreadsBox = React.createClass({
  loadThreadsFromServer: function () {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function (data) {
        this.setState({data: data})
      }.bind(this),
      error: function (xhr, status, err) {
        console.error(this.props.url, status, err.toString())
      }.bind(this)
    })
  },
  handleThreadSubmit: function (thread) {
    var threads = this.state.data
    var newThreads = threads.concat([thread])
    this.setState({data: newThreads})
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: thread,
      success: function (data) {
        this.setState({data: data})
      }.bind(this),
      error: function (xhr, status, err) {
        this.setState({data: threads})
        console.error(this.props.url, status, err.toString())
      }.bind(this)
    })
  },
  getInitialState: function () {
    return {data: []}
  },
  componentDidMount: function () {
    this.loadThreadsFromServer()
    setInterval(this.loadThreadsFromServer, this.props.pollInterval)
  },
  render: function () {
    return (
    <div className="threadsBox">
      <h1>Feed</h1>
      <div>
        <ThreadForm onThreadSubmit={this.handleThreadSubmit} />
      </div>
    </div>
    )
  }
})

module.exports = ThreadsBox

In Chrome developer tools, the error seems to be coming from this function:

 loadThreadsFromServer: function loadThreadsFromServer() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function (data) {
        this.setState({ data: data });
      }.bind(this),
      error: function (xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },

with the line console.error(this.props.url, status, err.toString() underlined.

Since it looks like the error seems to have something to do with pulling JSON data from the server, I tried starting from a blank db, but the error persists. The error seems to be called in an infinite loop presumably as React continuously tries to connect to the server and eventually crashes the browser.

EDIT:

I've checked the server response with Chrome dev tools and Chrome REST client, and the data appears to be proper JSON.

EDIT 2:

It appears that though the intended API endpoint is indeed returning the correct JSON data and format, React is polling http://localhost:3000/?_=1463499798727 instead of the expected http://localhost:3001/api/threads.

I am running a webpack hot-reload server on port 3000 with the express app running on port 3001 to return the backend data. What's frustrating here is that this was working correctly the last time I worked on it and can't find what I could have possibly changed to break it.

Answer

Bryan Field picture Bryan Field · May 17, 2016

The wording of the error message corresponds to what you get from Google Chrome when you run JSON.parse('<...'). I know you said the server is setting Content-Type:application/json, but I am led to believe the response body is actually HTML.

Feed.js:94 undefined "parsererror" "SyntaxError: Unexpected token < in JSON at position 0"

with the line console.error(this.props.url, status, err.toString()) underlined.

The err was actually thrown within jQuery, and passed to you as a variable err. The reason that line is underlined is simply because that is where you are logging it.

I would suggest that you add to your logging. Looking at the actual xhr (XMLHttpRequest) properties to learn more about the response. Try adding console.warn(xhr.responseText) and you will most likely see the HTML that is being received.