This error occurs when jwt.verify() receives an invalid or improperly formatted JSON Web Token. A valid JWT must have exactly three base64url-encoded parts separated by periods (header.payload.signature).
The "JsonWebTokenError: jwt malformed" error is thrown by the Node.js jsonwebtoken library when attempting to verify a token that doesn't conform to the standard JWT structure. A valid JWT must consist of exactly three components separated by dots: the header (containing metadata like algorithm and token type), the payload (containing claims and data), and the signature (ensuring token integrity). This error typically indicates that the token being passed to jwt.verify() is either null, undefined, corrupted, improperly extracted from headers, or missing one or more of its required components. The jsonwebtoken library performs strict validation and will reject any token that doesn't match the expected format. The error serves as a security measure, preventing the application from attempting to verify tokens that are clearly invalid or have been tampered with during transmission.
Add validation checks before calling jwt.verify() to ensure you're working with a valid token string:
const jwt = require('jsonwebtoken');
function verifyToken(token) {
// Check for null/undefined
if (!token) {
throw new Error('Token is missing');
}
// Check for string type
if (typeof token !== 'string') {
throw new Error('Token must be a string');
}
// Check for proper JWT format (3 parts separated by dots)
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error(`Invalid token format: expected 3 parts, got ${parts.length}`);
}
// Now verify
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
return decoded;
} catch (error) {
console.error('JWT verification failed:', error.message);
throw error;
}
}This pre-validation helps identify the exact issue before attempting cryptographic verification.
If you're reading the token from the Authorization header, ensure you're correctly stripping the "Bearer " prefix:
// Express middleware example
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
// Extract token after "Bearer "
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token required' });
}
try {
const user = jwt.verify(token, process.env.JWT_SECRET);
req.user = user;
next();
} catch (error) {
return res.status(403).json({ error: 'Invalid token' });
}
}Common mistake: Using authHeader.replace('Bearer ', '') which fails if the prefix is missing or has different casing.
Log the token value to inspect its structure and identify corruption:
function debugToken(token) {
console.log('Token value:', token);
console.log('Token type:', typeof token);
console.log('Token length:', token?.length);
if (token) {
const parts = token.split('.');
console.log('Token parts:', parts.length);
console.log('Header:', parts[0]?.substring(0, 20) + '...');
console.log('Payload:', parts[1]?.substring(0, 20) + '...');
console.log('Signature:', parts[2]?.substring(0, 20) + '...');
}
}You can also paste the token into https://jwt.io to validate its structure and decode the contents. A valid token should decode without errors and show three distinct sections.
If tokens are passed via cookies or query parameters, ensure proper extraction:
// Cookie-based token
const cookieParser = require('cookie-parser');
app.use(cookieParser());
app.get('/protected', (req, res) => {
const token = req.cookies.jwt; // or req.cookies['access_token']
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
res.json({ user: decoded });
} catch (error) {
res.status(403).json({ error: error.message });
}
});// Query parameter token (not recommended for production)
app.get('/protected', (req, res) => {
const token = req.query.token;
if (!token || typeof token !== 'string') {
return res.status(401).json({ error: 'Invalid token' });
}
// Verify token...
});Ensure cookies are parsed correctly and query parameters aren't arrays.
Remove any whitespace or control characters that might corrupt the token:
function cleanToken(token) {
if (!token) return null;
// Remove whitespace, newlines, and tabs
return token.trim().replace(/[
]/g, '');
}
// Usage
const rawToken = req.headers['authorization']?.split(' ')[1];
const cleanedToken = cleanToken(rawToken);
if (cleanedToken) {
const decoded = jwt.verify(cleanedToken, process.env.JWT_SECRET);
}This is especially important when tokens are stored in files, environment variables, or passed through forms where extra whitespace might be introduced.
Token Storage Considerations: When storing JWTs in localStorage or sessionStorage on the client side, ensure you're not accidentally stringifying the token twice (e.g., JSON.stringify(token)), which wraps it in quotes and breaks the format.
Frontend Integration: When using frameworks like React or Vue, verify that your HTTP client (axios, fetch) is properly including the Authorization header. Check browser DevTools Network tab to inspect the actual header value being sent.
Token Encoding Issues: If you're manually constructing JWTs (not recommended), ensure you're using base64url encoding (not standard base64). The difference is that base64url uses '-' and '_' instead of '+' and '/' and omits padding '=' characters.
Testing with Malformed Tokens: You can intentionally create malformed tokens for testing by removing dots or characters: const malformed = validToken.replace('.', '');. This helps verify your error handling logic.
Alternative Validation Libraries: Consider using dedicated validation libraries like express-jwt or passport-jwt which provide more robust token extraction and validation middleware with better error messages.
Security Note: Never disable JWT verification or skip validation in production, even temporarily. If you're seeing this error frequently, investigate the root cause rather than bypassing security checks.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams