balderdashy/sails · error
Streaming file uploads via `req.file()` are only available o
Error message
Streaming file uploads via `req.file()` are only available over HTTP with Skipper.
What it means
Sails replaces req.file() with a stub outside Skipper's HTTP handling (most commonly on socket.io virtual requests) that immediately responds 500 with this exact message. Streaming multipart file uploads only work over real HTTP requests where Skipper is installed as the body parser.
Source
Thrown at lib/router/index.js:524
// Extremely simple query string parser (`req.query`)
function qsParser(req,res,next) {
var queryStringPos = req.url.indexOf('?');
if (queryStringPos !== -1) {
req.query = _.merge(req.query, QS.parse(req.url.substr(queryStringPos + 1)));
}
else {
req.query = req.query || {};
}
next();
}
// Extremely simple body parser (`req.body`)
function bodyParser (req, res, next) {
// Set up a mock `req.file()` clarifying that req.file() is not available
// outside of the context of Skipper (i.e. in this case, most commonly from
// socket.io virtual requests).
req.file = function fileUploadsNotAvailable(){
return res.status(500).send('Streaming file uploads via `req.file()` are only available over HTTP with Skipper.');
};
var bodyBuffer='';
if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'DELETE'){
req.body = _.extend({}, req.body);
return next();
}
// Ensure that `req` is a readable stream at this point
if ( !(req instanceof Readable) ) {
return next(new Error('Sails Internal Error: `req` should be a Readable stream by the time `route()` is called'));
}
req.on('readable', function() {
var chunk;
while (null !== (chunk = req.read())) {
bodyBuffer += chunk;
}View on GitHub (pinned to 7b76422cc2)
Solutions
- Perform the upload over plain HTTP (fetch/XHR/form POST) instead of io.socket.post.
- Verify sails.config.http.middleware.order includes 'bodyParser' and that it maps to Skipper (sails-hook-requests defaults).
- Guard server-side: check req.is('multipart/form-data') and reject socket-originated uploads early.
- For socket clients, upload via a signed HTTP URL and only notify completion over the socket.
Example fix
// before
io.socket.post('/upload', formData, cb); // hits stub req.file()
// after
fetch('/upload', { method: 'POST', body: formData }).then(cb); Defensive patterns
Strategy: fallback
Validate before calling
function canUpload(req) {
return typeof req.file === 'function' &&
!req.file.toString().includes('fileUploadsNotAvailable') &&
req.is('multipart/form-data');
} Type guard
function skipperAvailable(req) { return typeof req.file === 'function' && req._fileparser instanceof Object; } Try / catch
if (typeof req.file !== 'function' || isVirtualRequest(req)) {
return res.badRequest('Uploads require an HTTP request');
}
try {
await req.file('avatar').upload({ dirname: dir }, cb);
} catch (e) {
return res.serverError(e);
} Prevention
- Always upload files via HTTP, never io.socket
- Keep bodyParser (Skipper) in config.http.middleware.order
- Check req.wantsJSON/socket origin before calling req.file()
- Provide an HTTP upload endpoint for socket-connected clients
When it happens
Trigger: Calling req.file('avatar').upload(...) inside a controller action reached via a socket.io virtual request (io.socket.post), or via any route path where Skipper's body parser is not active (e.g. bodyParser overridden in config.http.middleware).
Common situations: Attempting file upload through socket.io from sails.io.js client; custom middleware order in config/http.js that skips Skipper; calling upload endpoints from mobile SDKs over websockets.
Related errors
AI-assisted analysis of balderdashy/sails@7b76422cc2 (2026-09-01).
Data as JSON: /api/errors/e0630521193510c5.
Report an issue: GitHub.