Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node JS TypeError with `path.join` when serving webpage with Express

I've just installed Node JS (v0.10.0) on a netbook running Linux Peppermint Three. I have a file to run which has the following at the top:

var app = require('express').createServer(),
    io = require('socket.io').listen(app);

app.listen(8080);

// routing
app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});

The problem is that when I visit localhost:8080 I get the following:

TypeError: Arguments to path.join must be strings
    at path.js:360:15
    at Array.filter (native)
    at exports.join (path.js:358:36)
    at exports.send (/home/guy/Dropbox/Node/socket_io echo test/node_modules/express/node_modules/connect/lib/middleware/static.js:129:20)
    at ServerResponse.res.sendfile (/home/guy/Dropbox/Node/socket_io echo test/node_modules/express/lib/response.js:186:3)
    at usernames (/home/guy/Dropbox/Node/socket_io echo test/med.js:11:7)
    at callbacks (/home/guy/Dropbox/Node/socket_io echo test/node_modules/express/lib/router/index.js:272:11)
    at param (/home/guy/Dropbox/Node/socket_io echo test/node_modules/express/lib/router/index.js:246:11)
    at pass (/home/guy/Dropbox/Node/socket_io echo test/node_modules/express/lib/router/index.js:253:5)
    at Router._dispatch (/home/guy/Dropbox/Node/socket_io echo test/node_modules/express/lib/router/index.js:280:5)

The exact same file works on my Windows XP laptop but I haven't updated Node there yet (still running v0.8.15). So I don't know if it's my installation of Node on Linux (to which I'm new) that's the problem or the disparity between versions. Obviously I don't want to update Node on Windows if it's going to result in the same problem.

I've checked that Express is where it should be and that seems okay. I've tried re-installing it via npm. I've looked up the error (by searching for the first line above) and found mentions here and here and here, where all seem to be saying it's resolved.

Any ideas what else (if anything) I can try to get my simple page server working?

like image 950
guypursey Avatar asked Mar 21 '13 20:03

guypursey


2 Answers

I've encountered this as well, on node v0.10.2, while trying to upgrade from 0.6.14. The problem lies in the connect static middleware, and how it handles the paths and a possible regression in how path.join handles its args.

Here's the problematic code from connect

  // setup
  var maxAge = options.maxAge || 0
    , ranges = req.headers.range
    , head = 'HEAD' == req.method
    , get = 'GET' == req.method
    , root = options.root ? normalize(options.root) : null //<!-- should be ''
    , redirect = false === options.redirect ? false : true
    , getOnly = options.getOnly
    , fn = options.callback
    , hidden = options.hidden
    , done;

Later, when the path is joined, you end up with a null, causing the error under v0.10.2

  // join / normalize from optional root dir
  path = normalize(join(root, path));

Under node 0.8.21, you get this

> require('path').join(null, 'file.txt');
'file.txt'

Under node 0.10.2, you get this instead

> require('path').join(null, 'file.txt');
TypeError: Arguments to path.join must be strings
    at path.js:360:15
    at Array.filter (native)
    at Object.exports.join (path.js:358:36)
    at repl:1:17
    at REPLServer.self.eval (repl.js:110:21)
    at repl.js:249:20
    at REPLServer.self.eval (repl.js:122:7)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.EventEmitter.emit (events.js:95:17)
    at Interface._onLine (readline.js:202:10)

TL;DR

You can monkey-patch your code in the meantime to work around the issue.

Absolute path

var filepath = '/some/absolute/path/to/file.ext';
res.sendfile(path.basename(filepath), {root: path.dirname(filepath)}); 

or

Relative path

res.sendfile('file.ext', {root: __dirname})

Setting {root: ''} will fail the truthy test in the static middleware.

like image 116
mikegradek Avatar answered Nov 16 '22 01:11

mikegradek


The problem seemed to be an incompatibility with the new version of Node. I had to update Express to v3.1.0 and Socket.IO to v9.1.13. I also then had to update NodeJS on my Windows laptop to the latest version as installed on my Linux netbook, v0.10.1.

With everything across both computers up to date, the code (as quoted above) then needed changing to the following:

var app = require('express')(),
    server = require('http').createServer(app),
    io = require('socket.io').listen(server);

server.listen(8080);

// routing
app.get('/', function (req, res) {
    res.sendfile(__dirname + '/index.html');
});

Note the direct invocation of the Express module here, as opposed to the method call used previously. Also note that IO must listen to the server created around the Express invocation (i.e., not app as I had it before); this got around some problems with getting the Socket.IO script to launch on the client-side. At the time of writing I still don't fully understand how the script is accessed given where the page is being served from. I will try to come back and update this when I know more.

PS. I'm indebted to @robertklep who guided me through this, as you can probably see in the comments above. Thank you!

like image 44
guypursey Avatar answered Nov 15 '22 23:11

guypursey