You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js+Express(Pino)服务频繁报Unexpected token %错误排查求助

Troubleshooting the "Unexpected token %" Pino SyntaxError in Node.js/Express

Hey there, let's dig into this frustrating issue you're facing with your Node.js server using Pino logging. First, let's recap the error you're seeing for clarity:

SyntaxError: Unexpected token % at EventEmitter.Pino (/code/node_modules/pino/pino.js:144:19) at EventEmitter.child (/code/node_modules/pino/pino.js:284:10) at loggingMiddleware (/code/node_modules/pino-http/logger.js:45:32) at Layer.handle [as handle_request] (/code/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/code/node_modules/express/lib/router/index.js:317:13) at /code/node_modules/express/lib/router/index.js:284:7 at Function.process_params (/code/node_modules/express/lib/router/index.js:335:12) at next (/code/node_modules/express/lib/router/index.js:275:10) at /code/node_modules/express-boom/index.js:28:5 at Layer.handle [as handle_request] (/code/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/code/node_modules/express/lib/router/index.js:317:13) at /code/node_modules/express/lib/router/index.js:284:7 at Function.process_params (/code/node_modules/express/lib/router/index.js:335:12) at next (/code/node_modules/express/lib/router/index.js:275:10) at expressInit (/code/node_modules/express/lib/middleware/init.js:40:5) at Layer.handle [as handle_request] (/code/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/code/node_modules/express/lib/router/index.js:317:13) at /code/node_modules/express/lib/router/index.js:284:7 at Function.process_params (/code/node_modules/express/lib/router/index.js:335:12) at next (/code/node_modules/express/lib/router/index.js:275:10) at query (/code/node_modules/express/lib/middleware/query.js:45:5) at Layer.handle [as handle_request] (/code/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/code/node_modules/express/lib/router/index.js:317:13) at /code/node_modules/express/lib/router/index.js:284:7 at Function.process_params (/code/node_modules/express/lib/router/index.js:335:12) at next (/code/node_modules/express/lib/router/index.js:275:10) at Function.handle (/code/node_modules/express/lib/router/index.js:174:3) at EventEmitter.handle (/code/node_modules/express/lib/application.js:174:10) at Server.app (/code/node_modules/express/lib/express.js:39:9) at emitTwo (events.js:106:13) at Server.emit (events.js:191:7) at HTTPParser.parserOnIncoming [as onIncoming] (_http_server.js:546:12) at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)

1. What's triggering this error?

The Unexpected token % error points to a problem with how Pino parses log content. Here are the most likely root causes:

  • Unescaped % characters in log data: Pino uses % as a placeholder for formatted values (similar to printf). If your log content (like dynamic request data, user input, or API responses) includes an unescaped %, Pino tries to parse it as a placeholder but fails, throwing this syntax error.
  • Intermittent problematic requests: The error happens randomly because it only triggers when a request contains a % in data that gets passed to Pino (e.g., a query parameter, request body, or URL path). Restarting works temporarily until another such request comes in.
  • Version-specific bugs: Older versions of Pino or pino-http had known bugs handling special characters like % in log payloads. These issues are often fixed in newer releases.
  • Middleware interference: A preceding middleware might be modifying request/response data in unexpected ways, introducing unescaped % characters that Pino tries to process.

2. Fixes you can implement right now

Here are actionable steps to resolve the issue:

Escape % characters in log data

Before passing dynamic content to Pino, escape any % by replacing it with %%. You can create a simple utility function for this:

const escapeLogContent = (content) => {
  if (typeof content === 'string') {
    return content.replace(/%/g, '%%');
  }
  // Recursively escape string values in objects
  if (typeof content === 'object' && content !== null) {
    return Object.fromEntries(
      Object.entries(content).map(([key, val]) => [key, escapeLogContent(val)])
    );
  }
  return content;
};

// Use it when logging:
logger.info(escapeLogContent({ requestQuery: req.query }));

Upgrade Pino and pino-http

Update to the latest stable versions to fix known parsing bugs. Run these commands:

npm install pino@latest pino-http@latest

Validate and sanitize input data

Add validation for incoming requests (using libraries like Joi or Zod) to catch or sanitize unexpected special characters before they reach the logging layer. This not only fixes the Pino error but also improves overall security.

Enable Pino's safe mode

Some versions of Pino include a safe option that automatically handles problematic characters. Enable it when initializing your logger:

const pino = require('pino');
const logger = pino({
  safe: true
});

Add error catching to prevent server crashes

Wrap the pino-http middleware or add a global Express error handler to catch these syntax errors and log the problematic request details, so you can identify exactly what's triggering the issue:

app.use((err, req, res, next) => {
  if (err instanceof SyntaxError && err.message.includes('Unexpected token %')) {
    logger.error('Pino parsing error triggered by request:', {
      url: req.url,
      query: req.query,
      body: req.body
    });
    res.status(500).send('Server error');
  } else {
    next(err);
  }
});

Verify middleware order

Ensure that pino-http is placed after any middleware that modifies request data, but before your route handlers. This ensures Pino receives clean, processed data without unexpected modifications.

内容的提问来源于stack exchange,提问作者Sai Raman Kilambi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:47:24