Gatsby: [1.0] Strange behavior with file paths in graphql

Created on 10 May 2017  ยท  26Comments  ยท  Source: gatsbyjs/gatsby

I was having a bit of a struggle again getting my data to play along with graphql. Something like this is not a problem:

[{
  "coverImage": {
    "url": "http://example.com/assets/someimage.png"
  }
}]

Neither is:

[{
  "coverImage": {
    "url": "/assets/someimage.png"
  }
}]

But this:

[{
  "coverImage": {
    "url": "assets/someimage.png"
  }
}]

or this:

[{
  "coverImage": {
    "url": "assets/someimage.png"
  }
}]

or this:

[{
  "coverImage": {
    "filepath": "assets/someimage.png"
  }
}]

results in a schema where you can't query the field from coverImage anymore. If I do I get this error:

UNHANDLED REJECTION Error: You supplied a GraphQL document with validation errors:
Field "filepath" of type "File" must have a selection of subfields. Did you mean "filepath { ... }"?
    at Object.validate (/Users/me/project/node_modules/relay-compiler/lib/RelayValidator.js:19:17)

Does that make sense?

All 26 comments

I believe that certain fields that look like files are actually turned into file nodes which is why you are getting the missing field error. I'm not sure what the use case was for that behavior tho

Yeah, that's the issue...

The use case is/was markdown frontmatter e.g.

---
title: "yo, cool post"
coverImage: "./my-sweet-cover-image.jpg"
---

A post about pure awesomeness.

The coverImage would automatically be converted to a link to its fileNode so you could write queries to automatically pull in that image w/ resizing, etc.

But yeah, right now the check isn't safe as it takes anything that looks like a relative link to a file.

Probably what we should do is recurse up the node's parents and make sure that it has a File ancestor.

@KyleAMathews ah well I was planning to do something similar. The assets are downloaded and placed relative to my data json, and I'd like those images to go through the sharp plugins. It's just that my images now have more fields besides url like title, size, width, height, but those are not crucial. So for now I could just replace the image data objects with strings holding the relative path to the file. If I do that things should get picked up by the sharp plugins?

yup! You'll be able to write queries like:

{
  coverImage {
    filePath {
      childImageSharp {
        responsiveSizes(maxWidth: 640) {
          src
          srcSet
        }
     }
  }
}

Nice! That would have taken me a while to figure out ๐Ÿ™‚

I'm starting to understand the graphiql browser better now. I kept clicking on the function names / constructor, probably because their color stands out more. But then you only see the arguments and not the fields and methods (or whatever they're called) ๐Ÿ˜…

@KyleAMathews some of my data doesn't have a coverImage, and it should be optional. Is it possible to only make a query like that if the data is available? Or otherwise define a fallback?

The query returns null for coverImage which then leeds to this error:

 message: 'Path must be a string. Received undefined',
    locations: [ [Object] ],
    path: [ 'allWorkJson', 'edges', 1, 'node', 'coverImage' ] },
  { TypeError: Path must be a string. Received undefined
      at assertPath (path.js:7:11)
      at Object.resolve (path.js:1146:7)
      at resolve (/Users/me/project/node_modules/gatsby/dist/schema/infer-graphql-type.js:335:41)

So for the type WorkJson, none of the nodes have coverImage?

Can you make your question more specific? Queries are always optional since you have to write them :-)

@KyleAMathews For type WorkJson most of them have a coverImage set to a filepath, some don't have the property in the json source.

I'm querying all WorkJson for an overview page. So some of the data has coverImage set to a path and others return with coverImage set to null

When does the previous error happen then?

@KyleAMathews At bootstrap. So when the index page query is executed I guess?

I can get into the graphiql interface and execute the query there. I see the same error.

What line of code is that? There should never be an error querying null data. Unless ooo... is this when it tries to load the File node?

This is my graphiql query

{
  allWorkJson {
    edges {
      node {
        coverImage {
          childImageSharp {
            responsiveSizes (maxWidth: 200) {
              src
            }
          }
        }

      }
    }
  }
}

And this the output

{
  "data": {
    "allWorkJson": {
      "edges": [
        {
          "node": {
            "coverImage": {
              "childImageSharp": {
                "responsiveSizes": {
                  "src": "/c51fc37d9860daff27cb3ebd113d360b-maxWidth=200&quality=50&pngCompressionLevel=9&width=200.png"
                }
              }
            }
          }
        },
        {
          "node": {
            "coverImage": null
          }
        },
        {
          "node": {
            "coverImage": null
          }
        },
        {
          "node": {
            "coverImage": {
              "childImageSharp": {
                "responsiveSizes": {
                  "src": "/53e9433574ffb78ca12150cf9c58b9a2-maxWidth=200&quality=50&pngCompressionLevel=9&width=200.png"
                }
              }
            }
          }
        },
        {
          "node": {
            "coverImage": null
          }
        },
        {
          "node": {
            "coverImage": null
          }
        },
        {
          "node": {
            "coverImage": null
          }
        },
        {
          "node": {
            "coverImage": null
          }
        }
      ]
    }
  },
  "errors": [
    {
      "message": "Path must be a string. Received undefined",
      "locations": [
        {
          "line": 31,
          "column": 9
        }
      ],
      "path": [
        "allWorkJson",
        "edges",
        1,
        "node",
        "coverImage"
      ]
    },
    {
      "message": "Path must be a string. Received undefined",
      "locations": [
        {
          "line": 31,
          "column": 9
        }
      ],
      "path": [
        "allWorkJson",
        "edges",
        2,
        "node",
        "coverImage"
      ]
    },
    {
      "message": "Path must be a string. Received undefined",
      "locations": [
        {
          "line": 31,
          "column": 9
        }
      ],
      "path": [
        "allWorkJson",
        "edges",
        4,
        "node",
        "coverImage"
      ]
    },
    {
      "message": "Path must be a string. Received undefined",
      "locations": [
        {
          "line": 31,
          "column": 9
        }
      ],
      "path": [
        "allWorkJson",
        "edges",
        5,
        "node",
        "coverImage"
      ]
    },
    {
      "message": "Path must be a string. Received undefined",
      "locations": [
        {
          "line": 31,
          "column": 9
        }
      ],
      "path": [
        "allWorkJson",
        "edges",
        6,
        "node",
        "coverImage"
      ]
    }
  ]
}

@KyleAMathews I don't know. How can I find out?

The file + line number are in the stack trace

All I have is this error from before, but here is the full stracktrace:

GraphQL Error:
[ { TypeError: Path must be a string. Received undefined
      at assertPath (path.js:7:11)
      at Object.resolve (path.js:1146:7)
      at resolve (/Users/me/project/node_modules/gatsby/dist/schema/infer-graphql-type.js:335:41)
      at resolveOrError (/Users/me/project/node_modules/graphql/execution/execute.js:474:12)
      at resolveField (/Users/me/project/node_modules/graphql/execution/execute.js:460:16)
      at /Users/me/project/node_modules/graphql/execution/execute.js:275:18
      at Array.reduce (native)
      at executeFields (/Users/me/project/node_modules/graphql/execution/execute.js:272:42)
      at collectAndExecuteSubfields (/Users/me/project/node_modules/graphql/execution/execute.js:722:10)
      at completeObjectValue (/Users/me/project/node_modules/graphql/execution/execute.js:704:10)
      at completeValue (/Users/me/project/node_modules/graphql/execution/execute.js:601:12)
      at completeValueWithLocatedError (/Users/me/project/node_modules/graphql/execution/execute.js:519:21)
      at completeValueCatchingError (/Users/me/project/node_modules/graphql/execution/execute.js:494:21)
      at resolveField (/Users/me/project/node_modules/graphql/execution/execute.js:462:10)
      at /Users/me/project/node_modules/graphql/execution/execute.js:275:18
      at Array.reduce (native)
    message: 'Path must be a string. Received undefined',
    locations: [ [Object] ],
    path: [ 'allWorkJson', 'edges', 1, 'node', 'coverImage' ] },

Right โ€” when debugging, it's most often your (or our) code that's the problem. So ignore all the graphql-specific code and find the Gatsby specific line of code.

Which in this case is /Users/me/project/node_modules/gatsby/dist/schema/infer-graphql-type.js. Try opening that in your editor and go to line 335 and see what's happening there.

The error suggests we're calling a function with undefined so we need to add a check to fail early to prevent this.

this occurs for me when testing stuff. The resolvers have context which, in a few places, Gatsby assumes has a path on it, I presume because it should if the query is run correctly with the processed Page

@KyleAMathews line 335 is the last line in this fragment

// Look for fields that are pointing at a file โ€” if the field has a known
// extension then assume it should be a file field.
//
// TODO probably should just check if the referenced file exists
// only then turn this into a field field.
function inferFromUri(key, types) {
  var fileField = types.find(function (type) {
    return type.name === `File`;
  });

  if (!fileField) return;

  return {
    type: fileField.nodeObjectType,
    resolve: function resolve(node, a, _ref5) {
      var path = _ref5.path;

      var fieldValue = node[key];

      // Find File node for this node (we assume the node is something
      // like markdown which would be a child node of a File node).
      var parentFileNode = _.find(getNodes(), function (n) {
        return n.type === `File` && n.id === node.parent;
      });

      // Use the parent File node to create the absolute path to
      // the linked file.
      var fileLinkPath = slash(nodePath.resolve(parentFileNode.dir, fieldValue));

Yeah, looks like we just need to check if fieldValue exists and return null if not.

Could you create a PR for this?

(and check of course that it solves your problem)

Seems to solve that problem ๐Ÿ˜„ #952

But I am now also seeing another error since updating my 1.0 to the HEAD today. Maybe this looks familiar to you guys? Otherwise I'll investigate further. I'm seeing this in the browser (and the console) when I load the page

ReferenceError: plugins is not defined
    at module.exports (/Users/me/project/.cache/api-runner-ssr.js:11:17)
    at /Users/me/project/node_modules/gatsby/dist/utils/develop.js:83:55
    at Layer.handle [as handle_request] (/Users/me/project/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/me/project/node_modules/express/lib/router/index.js:317:13)
    at /Users/me/project/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/me/project/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/me/project/node_modules/express/lib/router/index.js:275:10)
    at middleware (/Users/me/project/node_modules/webpack-hot-middleware/middleware.js:26:48)
    at Layer.handle [as handle_request] (/Users/me/project/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/me/project/node_modules/express/lib/router/index.js:317:13)
    at /Users/me/project/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/Users/me/project/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/me/project/node_modules/express/lib/router/index.js:275:10)
    at expressInit (/Users/me/project/node_modules/express/lib/middleware/init.js:40:5)
    at Layer.handle [as handle_request] (/Users/me/project/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/me/project/node_modules/express/lib/router/index.js:317:13)

I haven't seen that error... it looks weird since api-runner-ssr is just for server rendering so should never be loaded into the browser.

Weird, it points to this line

// During bootstrap, we write requires at top of this file which looks like:
// var plugins = [
//   require('/path/to/plugin1/gatsby-ssr.js'),
//   require('/path/to/plugin2/gatsby-ssr.js'),
// ]

module.exports = function (api, args, defaultReturn) {
  // Run each plugin in series.
  var results = plugins.map(function (plugin) {

This sounds like it should be a new issue :-)

Having somehow similar issue! was trying to get my head around sharp-plugin using GatsbyGram. I've changed the data structure (image property as follow):

[
...
{
...
    "image": {
        "filepath": "images/3.jpg",
        "caption": "blablabla"
    }
...
}
...
]

and then query it like

{
  allPostsJson {
    edges {
      node {
        image{
          filepath {
            childImageSharp{
              responsiveSizes(maxWidth: 640){
                src
                srcSet
              }
            }
          }
        }
      }
    }
  }
}

and received

{
  "errors": [
    {
      "message": "Path must be a string. Received undefined",
      "locations": [
        {
          "line": 6,
          "column": 11
        }
      ],
      "path": [
        "allPostsJson",
        "edges",
        0,
        "node",
        "image",
        "filepath"
      ]
    },
    {
      "message": "Path must be a string. Received undefined",
      "locations": [
        {
          "line": 6,
          "column": 11
        }
      ],
      "path": [
        "allPostsJson",
        "edges",
        1,
        "node",
        "image",
        "filepath"
      ]
    },
    {
      "message": "Path must be a string. Received undefined",
      "locations": [
        {
          "line": 6,
          "column": 11
        }
      ],
      "path": [
        "allPostsJson",
        "edges",
        2,
        "node",
        "image",
        "filepath"
      ]
    }
  ],
  "data": {
    "allPostsJson": {
      "edges": [
        {
          "node": {
            "image": {
              "filepath": null
            }
          }
        },
        {
          "node": {
            "image": {
              "filepath": null
            }
          }
        },
        {
          "node": {
            "image": {
              "filepath": null
            }
          }
        }
      ]
    }
  }
}

anything I am missing? btw, all images exist in right directory.

@amirhouieh oh hmmm that's tricky. It's an error somewhere deep in Gatsby. Try looking through https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/schema/run-sift.js (where queries are run (mostly) as it looks like an error that's happening during the running of queries. It might be a bug so would love a PR fixing things!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mikestopcontinues picture mikestopcontinues  ยท  3Comments

signalwerk picture signalwerk  ยท  3Comments

3CordGuy picture 3CordGuy  ยท  3Comments

KyleAMathews picture KyleAMathews  ยท  3Comments

brandonmp picture brandonmp  ยท  3Comments