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

GAE Java 21升级后CookieCache.parseCookies()抛出空指针异常

GAE Java 21升级后NPE错误排查与修复

问题描述

按照官方升级指南将Google App Engine应用从Java 17升级到Java 21(使用旧版捆绑服务),昨日运行正常,今日所有请求失败,日志抛出如下空指针异常:

writeError: status=500, message=java.lang.NullPointerException, response=org.eclipse.jetty.ee10.servlet.ServletContextResponse@7abe8062
java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:233)
    at org.eclipse.jetty.server.CookieCache.parseCookies(CookieCache.java:253)
    at org.eclipse.jetty.server.CookieCache.getCookies(CookieCache.java:71)
    at org.eclipse.jetty.server.Request.getCookies(Request.java:581)
    at org.eclipse.jetty.session.AbstractSessionManager.resolveRequestedSessionId(AbstractSessionManager.java:1231)
    at org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:690)
    at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:851)
    at com.google.apphosting.runtime.jetty.ee10.AppEngineWebAppContext.handle(AppEngineWebAppContext.java:301)
    at org.eclipse.jetty.server.handler.HotSwapHandler.handle(HotSwapHandler.java:90)
    at org.eclipse.jetty.server.Handler$Wrapper.handle(Handler.java:740)
    at com.google.apphosting.runtime.jetty.CoreSizeLimitHandler.handle(CoreSizeLimitHandler.java:75)
    at org.eclipse.jetty.server.Server.handle(Server.java:181)
    at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:635)
    at com.google.apphosting.runtime.jetty.delegate.internal.DelegateConnection.lambda$handle$0(DelegateConnection.java:145)
    at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter$2.run(JettyServletEngineAdapter.java:142)
    at com.google.apphosting.runtime.jetty.delegate.internal.DelegateConnection.handle(DelegateConnection.java:141)
    at com.google.apphosting.runtime.jetty.delegate.DelegateConnector.service(DelegateConnector.java:49)
    at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:274)
    at com.google.apphosting.runtime.RequestRunner.dispatchServletRequest(RequestRunner.java:348)
    at com.google.apphosting.runtime.RequestRunner.dispatchRequest(RequestRunner.java:233)
    at com.google.apphosting.runtime.RequestRunner.run(RequestRunner.java:199)
    at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:273)
    at java.base/java.lang.Thread.run(Thread.java:1583)

异常栈无业务代码调用,无法定位修复方向。

排查与修复方案

1. 问题根源定位

这个NPE发生在Jetty的CookieCache.parseCookies方法中,是处理请求Cookie时某个参数为null导致的。结合GAE Java 21 runtime特性,核心原因是旧版捆绑服务与Jetty EE10的兼容性冲突——Java 21 runtime将Jetty版本升级到EE10,而旧版捆绑服务的会话、Cookie处理逻辑未适配新版本。

2. 可行修复步骤

  • 临时规避:禁用会话跟踪
    如果应用部分或全部接口不需要会话支持,可在web.xml中配置禁用会话跟踪:

    <!-- 全局禁用 -->
    <session-config>
      <tracking-mode>OFF</tracking-mode>
    </session-config>
    
    <!-- 针对特定Servlet禁用 -->
    <servlet>
      <servlet-name>YourTargetServlet</servlet-name>
      <servlet-class>com.your.package.YourTargetServlet</servlet-class>
      <session-config>
        <tracking-mode>OFF</tracking-mode>
      </session-config>
    </servlet>
    
  • 拦截修复异常Cookie
    临时添加Filter在Jetty会话处理前拦截请求,过滤或修复畸形Cookie:

    public class CookieCleanupFilter implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest httpReq = (HttpServletRequest) request;
            String cookieHeader = httpReq.getHeader("Cookie");
            
            if (cookieHeader != null) {
                // 移除包含null值的异常Cookie条目
                String cleanedHeader = cookieHeader.replaceAll("\\s*\\w+=null(;|$)", "").replaceAll(";;", ";");
                if (cleanedHeader.endsWith(";")) {
                    cleanedHeader = cleanedHeader.substring(0, cleanedHeader.length() - 1);
                }
                
                // 包装请求替换Cookie头
                HttpServletRequestWrapper wrappedReq = new HttpServletRequestWrapper(httpReq) {
                    @Override
                    public String getHeader(String name) {
                        return "Cookie".equalsIgnoreCase(name) ? (cleanedHeader.isEmpty() ? null : cleanedHeader) : super.getHeader(name);
                    }
                };
                chain.doFilter(wrappedReq, response);
                return;
            }
            
            chain.doFilter(request, response);
        }
    }
    

    在web.xml中注册Filter,确保它在SessionHandler之前执行:

    <filter>
      <filter-name>CookieCleanupFilter</filter-name>
      <filter-class>com.your.package.CookieCleanupFilter</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>CookieCleanupFilter</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  • 迁移到新版捆绑服务
    彻底解决问题的方案是逐步迁移到GAE新版捆绑服务(如用Cloud Tasks替代旧版Task Queue、Cloud Storage替代Blobstore等),新版服务完全适配Java 21 runtime的Jetty环境。

  • 请求官方支持
    若上述方法无效,直接向Google Cloud提交工单,提供完整日志、应用配置和升级步骤,请求官方修复runtime兼容性问题。

内容的提问来源于stack exchange,提问作者Antoine Snyers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 17:37:10