name: tanstack-start-vps-startup-script
description: Fix "Cannot read properties of undefined (reading 'fetch')" when deploying TanStack Start to VPS — server.fetch vs server.default.fetch gotcha
triggers:
- tanstack start server not starting
- Cannot read properties of undefined reading fetch
- tanstack start EADDRINUSE 3010
TanStack Start VPS Startup Script
The Gotcha
When you import the TanStack Start server build:
`js
import server from './dist/server/server.js'
`
You get:
- -
server.fetch→ the fetch handler function ✓ - -
server.default→ exists as a plain object - -
server.default.fetch→ undefined ✗
The fetch handler is directly on the imported module as server.fetch, NOT under .default.
Working start-server.mjs
`js
import { createServer } from 'node:http';
import server from './dist/server/server.js';
const PORT = parseInt(process.env.PORT || '3010', 10);
const HOST = process.env.HOST || '0.0.0.0';
const httpServer = createServer(async (req, res) => {
try {
const baseUrl = http://${req.headers.host || ${HOST}:${PORT}};
const url = new URL(req.url, baseUrl);
// GET/HEAD cannot have body — must be null
const bodyAllowed = req.method !== 'GET' && req.method !== 'HEAD';
let body = null;
if (bodyAllowed) {
body = await new Promise((resolve, reject) => {
const chunks = [];
req.on('data', chunk => chunks.push(chunk));
req.on('end', () => resolve(Buffer.concat(chunks)));
req.on('error', reject);
});
}
const webReq = new Request(url, {
method: req.method,
headers: req.headers,
body,
duplex: 'half',
});
// Use server.fetch directly — NOT server.default.fetch
const webRes = await server.fetch(webReq);
const status = webRes.status;
const responseHeaders = Object.fromEntries(webRes.headers.entries());
res.writeHead(status, responseHeaders);
if (webRes.body) {
for await (const chunk of webRes.body) {
res.write(chunk);
}
}
res.end();
} catch (err) {
console.error('Request error:', err.message);
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('Internal Server Error');
}
});
httpServer.listen(PORT, HOST, () => {
console.log(Server running on ${HOST}:${PORT});
});
process.on('SIGTERM', () => httpServer.close(() => process.exit(0)));
`
Common error patterns
| Error | Cause |
| ------- | ------- |
Cannot read properties of undefined (reading 'fetch') | Used server.default.fetch instead of server.fetch |
Request with GET/HEAD method cannot have body | Passed req (stream) as body for GET requests — must be null |
EADDRINUSE 0.0.0.0:3010 after killing old process | Old node process respawned (systemd/docker), use fuser -k 3010/tcp or find and kill parent |
Port conflicts
Before starting, always kill existing processes:
`bash
fuser -k 3010/tcp 2>/dev/null; sleep 1
`
Verify method
Test with direct curl before Nginx proxy:
`bash
curl -s http://127.0.0.1:3010/ -I | head -5
Should show HTTP/1.1 200 OK (or redirect)
`
Build first if routes changed
If you add new API routes, rebuild first:
`bash
cd /path/to/app && nvm use 20 && npm run build
`
Then restart the server.