当前位置: 首页 > news >正文

Jenkins Git Parameter The default value has been returned 排查与修复

Jenkins Git Parameter "The default value has been returned" 排查与修复

某天测试同学在 Jenkins 上构建 Android 包,参数页一打开就跳出一连串红色错误,分支下拉框是空的,只剩一个默认值 origin/master

The default value has been returned
An error occurred while download data
Error performing git command: git ls-remote -h git@git.example.com:org/mobile-app.git
Please look at the Log
Please check the configuration

构建本身还能跑——只要硬着头皮选默认 origin/master 提交,git fetch 是成功的。但分支选择没了,等于每次都得在备注里手填要打的分支,谁都不能接受。

这篇记录一次跑歪两次才找到真凶的排查。结论很反直觉:根因跟 git、跟 SSH 凭据、跟仓库授权全都没关系,是 Java 主进程内存里的旧二进制跟磁盘上被 apt 静默升级过的 jspawnhelper 对不齐。

环境

组件 版本
OS Ubuntu 22.04 LTS
OpenJDK 17.0.19+10-1~22.04.2
Jenkins 主进程 同一 PID 自 3 月 20 日运行至今(事发是 5 月 29 日,跑了 70 天没重启)
Git plugin git@5.2.1
Git Parameter plugin git-parameter@0.9.19
Job SCM URL git@git.example.com:org/mobile-app.git(SSH 协议)
远端 Gitea 自建

公司内部域名/IP 已替换成示意值,读者可对号入座。

第一反应:怀疑 SSH / 仓库授权(错的方向)

Error performing git command: git ls-remote 这条最显眼,本能先排查 SSH。登到 Jenkins controller 上以 jenkins 用户 sanity check:

# su - jenkins 切到 jenkins 用户的登录态
$ ssh -o BatchMode=yes -T git@git.example.com
Hi there, root! You've successfully authenticated with the key named jenkins-test,
but Gitea does not provide shell access.$ git ls-remote -h git@git.example.com:org/mobile-app.git
5fcd08eb861062181b981bee3a4d431307e9a721    refs/heads/ch_1.12.0
ebc6fd0195bcfd484e5cf063f3d8b33e79cc86a4    refs/heads/dev
... (一长串分支)

双双跑通。 SSH key 是在 Gitea 上、对应 repo 也有权限,命令行 ls-remote 没问题。

排除 SSH。继续往下。

拐弯:拉 Job 的 config.xml 看 SCM 配置(自圆其说也是错的)

走过这条弯路要写出来——它是后面真正定位的反面对照。

走 Jenkins REST API 把 job 配置抠出来:

curl -sS --user "$USER:$TOKEN" \"http://jenkins.example.com:8080/job/mobile-android-build/config.xml"

里面的 SCM 节点是这样的:

<scm class="hudson.plugins.git.GitSCM" plugin="git@5.2.1"><userRemoteConfigs><hudson.plugins.git.UserRemoteConfig><url>git@git.example.com:org/mobile-app.git</url><!-- 注意:没有 <credentialsId> 这个子节点 --></hudson.plugins.git.UserRemoteConfig></userRemoteConfigs>...
</scm>

一眼瞟到没 <credentialsId>,就脑补了一套很顺的解释:

Git Parameter 插件的 ls-remote 是在 Jenkins controller 上跑的(不是在 agent 上),而 SCM 没有 credentialsId,所以它没凭据可用,所以 ls-remote 失败 → 退回默认值。

这套解释挺自洽,但完全是错的。因为前面已经验证了:以 jenkins 用户身份命令行跑 ls-remote 一切正常——不带任何凭据声明,靠的就是 jenkins 用户 ~/.ssh/id_rsa 的隐式认证。Git Parameter 插件的 git-client 子进程是 fork 自 Jenkins JVM,环境跟登录态 shell 一致,它本来也能用同一把 key

这个错误推断我特意保留下来——它提醒一件事:配置文件里看到一处"明显的缺失",不等于那处缺失就是当前症状的因。 得有动态证据。

真正的根因:扫 Jenkins 日志

Jenkins 系统日志走 systemd journal:

$ journalctl -u jenkins --since "1 hour ago" \| grep -E "git.?param|exception|severe"

第一条 SEVERE 直接打脸:

SEVERE  n.u.l.h.p.g.GitParameterDefinition#generateContents:[ mobile-android-build ]  Unexpected error!java.io.IOException: error=0, Failed to exec spawn helper: pid: 2052812, exit value: 1
Caused: java.io.IOException: Cannot run program "git": error=0,Failed to exec spawn helper: pid: 2052812, exit value: 1
Caused: hudson.plugins.git.GitException:Error performing git command: git ls-remote -h git@git.example.com:org/mobile-app.gitat net.uaznia.lukanus.hudson.plugins.gitparameter.GitParameterDefinition.getBranch(...)at net.uaznia.lukanus.hudson.plugins.gitparameter.GitParameterDefinition.generateContents(...)

关键字:Failed to exec spawn helper, exit value: 1。这不是 git 在抱怨,是 JVM 想 fork 一个子进程,但 fork 出来的「spawn helper」起不来。

继续在 journal 里翻:

jenkins[2054134]: jspawnhelper version 17.0.19+10-1-22.04.2-Ubuntu
jenkins[2054134]: This command is not for general use and should only be runas the result of a call to ProcessBuilder.start() orRuntime.exec() in a java application

jspawnhelper 这个二进制是 JDK 自带的一个小工具,专门给 JVM 在 Linux 上启动子进程用。它正常情况下绝对不会自己打日志——它接 stdin/stdout 的文件描述符,干完活就退。日志里看到它说"这个命令不是给一般人用的",意味着它被启动时 fd 没有按预期接好,于是它把 usage 打到 stderr 然后退出 1

另一个反常对得上:还能注意到事故时段连 mobile-ios-build 同样的报错都有——不是 Android job 独有,不是这个 git 仓库独有。任何 job 只要走 git-client 插件 fork 子进程 git ls-remote,都会撞。

到这里已经基本能锁定方向:JVM 启动子进程的这条路径整个坏了。

解释:长跑 JVM 撞 apt 静默升级

ps -ef | grep jenkins 看主进程:

jenkins   948661       1  1 Mar20 ?        18:21:05 /usr/bin/java -jar /usr/share/java/jenkins.war ...

Mar20 起的,到 5 月 29 日整整 70 天没重启过 JVM

而 Ubuntu 22.04 默认装了 unattended-upgrades,每天自动跑安全补丁。openjdk-17 是 main 仓库的"重要安全更新"白名单包,发版就升。这 70 天里 OpenJDK 至少被升级过一次到现在的 17.0.19

但是——升级 openjdk-17-jre-headless 这个 .deb 包,apt 不会重启正在跑的 java 进程。 结果就是:

  • Jenkins JVM 进程在内存里跑的,还是 70 天前那个版本的 java 二进制(Linux 允许打开后被 unlink,旧文件镜像跟着进程的虚拟内存映射继续存活)。
  • 磁盘上 /usr/lib/jvm/java-17-openjdk-amd64/lib/jspawnhelper 已经被换成新版
  • JVM 内部 fork 子进程时,走的代码路径里写死了 jspawnhelper 的路径,用旧版 JVM 去 exec 新版 jspawnhelper,两者交换 fd 的握手协议对不齐 → helper 收不到正确的 fd → 直接 print usage 退 1。

这就是 error=0, Failed to exec spawn helper, exit value: 1 的完整因果链。

这个坑不限 Jenkins,任何 long-running JVM(GitLab、Nexus、ES、Kafka 都中过)都会撞——只要:

  1. 跑在 OpenJDK 上
  2. 用 systemd 起,但服务 unit 没有响应 apt 触发自动重启
  3. 用到 Runtime.exec() / ProcessBuilder 启动子进程

修复:重启 JVM

修复只有一句:让 Jenkins 主进程整体换掉,重新加载新版 java + jspawnhelper

systemctl restart jenkins

注意是 restart,不是 reload。reload 只让 Jenkins 重新解析 config.xmlJVM 进程不换,治不了。Jenkins UI 上的 Safe Restart / Restart 同样能修,本质都是 fork 出新 JVM。

操作前确认队列里没 in-flight build:

curl -sS -G --user "$USER:$TOKEN" \--data-urlencode 'tree=computer[displayName,executors[currentExecutable[url]]]' \http://jenkins.example.com:8080/computer/api/json

所有 currentExecutable 都是 null 就能放心 restart。

重启完,进 Mobile-Android job 的 build 参数页,分支下拉框秒出全量分支列表。journalctl -u jenkins -f 也再无 Failed to exec spawn helper 出现。

防护:把 openjdk-17 钉死,不让 apt 偷升

要根治"以后还会再撞"这个隐患,最直接的办法是把 openjdk-17-* 这几个包从自动升级里排除

# 先看实际装了哪些
$ dpkg -l 'openjdk-*' | awk '/^ii/ {print $2, $3}'
openjdk-17-jdk:amd64           17.0.19+10-1~22.04.2
openjdk-17-jdk-headless:amd64  17.0.19+10-1~22.04.2
openjdk-17-jre:amd64           17.0.19+10-1~22.04.2
openjdk-17-jre-headless:amd64  17.0.19+10-1~22.04.2# 全部 hold
$ apt-mark hold openjdk-17-jdk openjdk-17-jdk-headless \openjdk-17-jre openjdk-17-jre-headless# 验证
$ apt-mark showhold
openjdk-17-jdk
openjdk-17-jdk-headless
openjdk-17-jre
openjdk-17-jre-headless

为什么不直接关掉整个 unattended-upgrades?因为这台机器还跑着别的服务,kernel/openssl/sudo 之类的 CVE 自动补丁该打还得打——只把 JDK 钉死,影响范围最小。

以后想主动升 JDK:

apt-mark unhold 'openjdk-17-*'
apt update && apt upgrade
systemctl restart jenkins   # 关键:升完一定要重启 JVM
apt-mark hold openjdk-17-jdk openjdk-17-jdk-headless \openjdk-17-jre openjdk-17-jre-headless

把 "升级 JDK 后必须立即 restart jenkins" 写进 runbook,是这套方案的关键配套——不写就是把同一个雷又埋回去。

配套监控:早发现一次告警都不亏

Failed to exec spawn helper 是个非常明确的字符串,加一条 journalctl 监听足以兜底——万一以后哪天因为 hold 漏了某个包又中招,告警先到运维手里,而不是测试同学先在 UI 上撞红:

journalctl -u jenkins -f \| grep --line-buffered 'Failed to exec spawn helper' \| while read line; docurl -X POST ... # 推企业 IMdone

复盘:为什么会走两次弯路

排查这条问题花的时间,大头都不在"找堆栈"那一步——而在前面两次自圆其说

  1. 第一次:看到 git ls-remote 字样就锁定 SSH/凭据方向。命令行验证 SSH 通了之后,没有第一时间放弃这个方向,反而开始想"是不是 controller 端某种特殊执行路径绕过了 SSH"。
  2. 第二次:从 config.xml 里看到 SCM 缺 <credentialsId>,立刻脑补了一套"Git Parameter 跑在 controller 没凭据所以失败"的解释——这套理论既能解释当前症状,又指向一个看得见的缺失,人会本能停下不再查

真正的破局点是 journalctl。 错误堆栈里"Failed to exec spawn helper"这一行跟 git/SSH/凭据毫无关联——它指向的是更下层的 JVM 进程管理。如果第一时间扫日志,省下 30 分钟全在用错的模型解释现象。

教训写成两条:

命令行手动复现一切正常时,别再在"命令本身"上反复找原因。症状必然在被命令上层的某个调用者——它的环境跟你手动 shell 不一样。

看 config 文件看出"明显缺失"时,先验证那处缺失是不是当前症状的因。配置缺失常年存在却今天才暴露,多半不是它本身的事。

快速参考

报错对照

现场 关键字
UI 红字 The default value has been returned / An error occurred while download data / Please check the configuration
Jenkins 日志堆栈 Failed to exec spawn helper, exit value: 1 / Cannot run program "git"
systemd journal 旁路 jspawnhelper[...]: This command is not for general use ...

修复命令

# 1. 确认没有 in-flight build
curl -sS -G --user "$USER:$TOKEN" \--data-urlencode 'tree=computer[executors[currentExecutable[url]]]' \http://jenkins.example.com:8080/computer/api/json# 2. 重启 JVM(不是 reload)
systemctl restart jenkins

防护命令

# 钉死 OpenJDK 自动升级
apt-mark hold openjdk-17-jdk openjdk-17-jdk-headless \openjdk-17-jre openjdk-17-jre-headless
apt-mark showhold

人工升级 JDK 流程

apt-mark unhold 'openjdk-17-*'
apt update && apt upgrade
systemctl restart jenkins     # 不能省
apt-mark hold openjdk-17-jdk openjdk-17-jdk-headless \openjdk-17-jre openjdk-17-jre-headless

铁律

  • reload 不能修——只有换 JVM 进程才能修。
  • 升 JDK 必 restart——把这条写进 runbook,违反就是埋雷。
  • unattended-upgrades 默认不会重启你的服务——长跑 JVM 全部都要面对这个责任。
  • 以后再撞 "Failed to exec spawn helper" 不要再先怀疑业务层——这五个字直接指向 JVM 进程 + JDK 包版本错配。
http://www.jsqmd.com/news/909440/

相关文章:

  • Arduino入门教程十八|光骑士LED追逐动画(shiftOut位序详解+左右移位运算符+移位寄存器动态特效)
  • 为什么游戏修改器总要付费?WandEnhancer的免费完整解决方案
  • 从VOC到YOLO:手把手教你构建目标检测数据集(含自动划分train/val/test)
  • DIY百元级焊锡烟雾净化器:从原理到制作全解析
  • 2026年常州钻石回收正规回收优选:添价收全国连锁稳居第一 - 薛定谔的梨花猫
  • 3分钟实现GitHub下载提速:这款免费浏览器插件如何让代码获取效率翻倍
  • 从选型到调试:一份给硬件工程师的SiPM实战避坑指南(附滨松/灵明光子参数对比)
  • 抖音上的视频怎么去水印保存?2026最新方法实测
  • 近一年丽江目的地婚礼哪家好?主流厂商预算分档解析 - 资讯纵览
  • 3个步骤将手机摄像头变成专业直播源
  • AzurLaneAutoScript:碧蓝航线全自动脚本助手终极指南
  • 基于STM32的模型火箭飞控系统设计:从硬件选型到软件实现
  • 快速迭代的AI项目中如何利用Taotoken灵活调整模型与预算
  • 2026年5月西安曲江宠物清洁服务推荐哪家好?宠物毛发清洁除味消毒正规机构盘点 - 速递信息
  • 告别手动调参!详解MATLAB STernary工具箱中那些你不知道的‘隐藏’高级设置
  • 魔兽争霸III重生之路:WarcraftHelper如何让经典游戏在现代电脑上完美运行
  • 视频认证:从源头构建可信数字内容的技术原理与实践
  • 2026年纯水机:解读行业三大核心发展趋势 - 速递信息
  • 2026年高温湿度仪主流品牌推荐哪家?国产优质仪器选购指南 - 品牌推荐大师
  • 从CD光盘到手机屏幕:聊聊你身边无处不在的‘光的衍射’现象
  • 终极指南:5分钟快速安装ViGEmBus虚拟手柄驱动,告别游戏兼容性烦恼
  • PAT天梯赛L2-045‘堆宝塔’:图解双栈(柱)思想与完整避坑指南
  • Python多线程编程实战:从GIL原理到树莓派传感器数据采集
  • 2026年上海超声波焊接机厂家深度横评:德汇联、灵科、骄成、必勒、岳友全方位对比指南 - 年度推荐企业名录
  • Arduino驱动WS2812B灯带:从硬件原理到HSV动画编程全解析
  • Go语言机器学习工程实践:构建生产级AI系统
  • 比特币技术原理与人性化挑战:从价值存储到心理建设
  • 2026年Q2中国拆除项目优质厂家首选推荐:合肥新建物资回收有限公司电话13866761254 - 安互工业信息
  • 如何永久保存你的微信聊天记录?3步掌握数据主权
  • 别再用循环算差值了!NumPy的np.diff函数5分钟搞定数据前后项差分