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

Jenkins升级至2.319后Pipeline读取工作区文件触发空指针异常

问题背景

在Jenkins 2.187旧版本环境中,以下Pipeline脚本可以正常拉取Git仓库全量文件,核心逻辑如下:

node {

    stage('checkout spring-cloud-config') {
        dir('config') {
            checkout scm
        }
    }

    stage('csv -> yml') {
        def files = new File("${workspace}/config/security").listFiles()
        for(int i = 0; i < files.size(); i ++){
...
}

将Jenkins升级迁移到2.319版本后,同一段脚本运行失败,报错日志如下:

[Pipeline] }
[Pipeline] // dir
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (csv -> yml)
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
java.lang.NullPointerException: Cannot invoke method size() on null object
    at org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:91)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:48)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.NullCallSite.call(NullCallSite.java:35)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:163)
    at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:165)
    at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:17)
    at WorkflowScript.run(WorkflowScript:16)
    at ___cps.transform___(Native Method)
    at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:86)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:113)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixName(FunctionCallBlock.java:78)
    at jdk.internal.reflect.GeneratedMethodAccessor263.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
    at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
    at com.cloudbees.groovy.cps.Next.step(Next.java:83)
    at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:174)
    at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:163)
    at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:129)
    at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:268)
    at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:163)
    at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:18)
    at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:51)
    at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:185)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:403)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$400(CpsThreadGroup.java:97)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:315)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:279)
    at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:67)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:139)
    at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
    at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:68)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)
Finished: FAILURE

需要确认该问题是Groovy版本更新导致,还是缺少相关Jenkins插件引发。


问题根因

这个报错既不是Groovy版本更新导致,也不是缺少核心运行插件,本质是高版本Jenkins收紧了Pipeline脚本安全沙箱规则、强化了分布式构建上下文隔离,原有脚本的不规范写法在新版本下不再兼容:

  1. Pipeline中直接使用Groovy原生new File()操作文件属于错误用法:这类原生文件API直接读取Jenkins控制器节点的本地文件系统,不会自动识别当前构建任务所在的代理节点工作目录,也不在脚本安全沙箱的默认放行白名单里。高版本Jenkins默认开启严格沙箱模式,这类操作要么被权限拦截,要么读取到错误的路径,当listFiles()找不到对应路径时会直接返回null,后续调用size()就会触发空指针。
  2. 硬拼接${workspace}/config/security路径不可靠:高版本Pipeline中dir('config')步骤切换的是Pipeline步骤级别的相对工作路径,不会直接在控制器文件系统生成对应层级的物理目录,拼接出来的路径本身不存在,自然无法获取文件列表。
修复方案
  • 彻底弃用Pipeline中的原生new File()文件操作,统一使用Jenkins官方提供的Pipeline内置步骤处理文件,这类步骤会自动适配当前构建节点的工作空间,默认兼容沙箱规则。
  • 遍历文件优先使用findFiles步骤(依赖Pipeline Utility Steps插件,属于Pipeline生态常用基础插件,高版本环境一般默认预装),调用返回结果前增加空值判断,避免空指针。
  • 修正后的参考代码:
node {
    stage('checkout spring-cloud-config') {
        dir('config') {
            checkout scm
        }
    }

    stage('csv -> yml') {
        // 自动匹配当前工作空间下的对应路径,兼容分布式构建场景
        def files = findFiles(glob: 'config/security/*')
        if (files) {
            for(int i = 0; i < files.size(); i ++){
                // 后续文件读写、处理逻辑统一使用readFile、writeFile等内置步骤实现
                // ...
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:48:15