8

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?

Community
  • 1
  • 1
guypursey
  • 3,114
  • 3
  • 24
  • 42
  • 3
    Since you're using `require('express').createServer()`, are you sure your Express is up-to-date? Express 3.x uses `require('express')()`. Also, try and see if it works if you run it from a directory which doesn't contain spaces in the name (`socket_io test echo`). Your example works fine in Node 0.10.1 and Express 3. – robertklep Mar 21 '13 at 20:40
  • I ran `npm view express version` in the terminal for that folder and got 3.1.0, same as on Windows. I've also put some underscores in place of the spaces in the folder name but it still hasn't solved the problem. I tried removing `.createServer` but that threw a TypeError (`object is not a function`) so now not sure if the version number showing up is correct, if what you say is true. Thanks for the suggestions. – guypursey Mar 21 '13 at 21:35
  • 1
    `npm view` shows the version of the package in the NPM registry, not the version you have installed locally; try `npm list express` instead. – robertklep Mar 22 '13 at 06:11
  • Thanks for that. You're right; Express was out of date. The problem was in 'package.json' which I had to amend to 3.1.0 to update. As a result, I've changed my code to `require('express')()`. However, I still get the original problem. As the problem seems to be with the call `res.sendfile()` (at, as shown above, `11:7`), I wonder if it's something to do with my use of `__dirname`, since if that is undefined then that might be creating the string/arguments problem... There's no mention of `__dirname` in [the relevant part of the API reference](http://expressjs.com/api.html#res.sendfile). – guypursey Mar 22 '13 at 09:11
  • 1
    [__dirname is a Node global](http://nodejs.org/docs/latest/api/globals.html#globals_dirname). Try `console.log(__dirname + '/index.html')` in your route handler to see if it actually contains a valid path. – robertklep Mar 22 '13 at 10:55
  • Thanks for pointing that out and for the tip. It turned out that socket.io needed updating too and then I had to pull Node on my Windows machine up to date in order for that to work. My original problem is gone but I now have another in that 'index.html' cannot access the script. Whereas including `` towards the top of 'index.html' worked before, and is advised on [the Socket IO website](http://socket.io/) it is now unable to find the file. I'm not sure how I'm going to rephrase my question to account for this! Thanks for all your help so far. – guypursey Mar 22 '13 at 15:02
  • 1
    Try searching SO for socket.io.js not being found, I remember seeing that question being asked before :) – robertklep Mar 22 '13 at 15:23
  • Done :-) I had to read a lot of questions but I finally found [this one](http://stackoverflow.com/questions/14258349/socket-io-is-not-served-in-socket-io-socket-io-js) which worked for me. Thanks so much for your help. It's a shame I can't mark your answer correct. I suspect first I will have to work out how to rephrase my question so that it's useful to others... Or just close it. Anyway, thanks again :-) – guypursey Mar 22 '13 at 17:20
  • 1
    You can answer your own question, on how you solved the problems. Might help someone in the future :) – robertklep Mar 22 '13 at 17:47
  • I have done that now. I've given you credit too for helping me through this :-) – guypursey Mar 24 '13 at 18:44

3 Answers3

13

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.

mikegradek
  • 359
  • 2
  • 6
  • +1 for digging in so deep into connect. @perropicante - I can go with the guypursey answer below which solves my problem but can you provide a little more details about your approach to debug this so deep, how did you actually found that the problem is with connect. Thanks.. – Anmol Saraf Aug 29 '13 at 19:41
  • @AnmolSaraf unfortunately, there was no magic to it. I used a debugger, and stepped through. I had tests covering this case (indirectly) prior to the upgrade, and noticed they were failing, so I tried a few similar corner cases, and narrowed it down to `require('path').join(null, 'file.txt');` – mikegradek Nov 04 '13 at 03:37
5

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!

Community
  • 1
  • 1
guypursey
  • 3,114
  • 3
  • 24
  • 42
0

Quick fix

sed -i bak -e  's/\/\/.join/if(root\ ==\ null){root="";}\/\//g' node_modules/express/node_modules/connect/lib/middleware/static.js
Pablo Moretti
  • 1,374
  • 13
  • 10