javastringjenkinsjenkins-pipelinejenkins-groovy

Why does jenkins readyaml stop working after you create a StringJoiner?


I created the following script

import groovy.json.*

pipeline {
    agent { node { label 'mycomputer' } }
    stages {
        stage ( 'Test') {
            steps{
                script {
                    StringJoiner a = new StringJoiner(",");
                    a.add("a").add("b").add("c")
                    env.out = a.toString()
                    echo env.out
                    datas = readYaml file: "/home/cloud-user/test.yaml"
                }
            }
        }
    }
}

This results in this output:

[Pipeline] { [Pipeline] stage [Pipeline] { (Test) [Pipeline] script [Pipeline] { [Pipeline] echo a,b,c [Pipeline] readYaml [Pipeline] } [Pipeline] // script [Pipeline] } [Pipeline] // stage [Pipeline] } [Pipeline] // node [Pipeline] End of Pipeline an exception which occurred: in field com.cloudbees.groovy.cps.impl.BlockScopeEnv.locals in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@7bb36fb8 in field com.cloudbees.groovy.cps.impl.CallEnv.caller in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@636cffca in field com.cloudbees.groovy.cps.Continuable.e in object org.jenkinsci.plugins.workflow.cps.SandboxContinuable@2664c301 in field org.jenkinsci.plugins.workflow.cps.CpsThread.program in object org.jenkinsci.plugins.workflow.cps.CpsThread@1339b8f9 in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.threads in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@146a9a96 in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@146a9a96 Also: org.jenkinsci.plugins.workflow.actions.ErrorAction$ErrorId: 16a49acb-27b5-4869-b5ae-bfdb1ca5e058 Caused: java.io.NotSerializableException: java.util.StringJoiner at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:278) at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65) at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56) at org.jboss.marshalling.MarshallerObjectOutputStream.writeObjectOverride(MarshallerObjectOutputStream.java:50) at org.jboss.marshalling.river.RiverObjectOutputStream.writeObjectOverride(RiverObjectOutputStream.java:179) at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:341) at java.base/java.util.HashMap.internalWriteEntries(HashMap.java:1858) at java.base/java.util.HashMap.writeObject(HashMap.java:1412) at org.jboss.marshalling.reflect.JDKSpecific$SerMethods.callWriteObject(JDKSpecific.java:134) at org.jboss.marshalling.reflect.SerializableClass.callWriteObject(SerializableClass.java:231) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1128) at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:271) at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1182) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1140) at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:271) at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1182) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1140) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1119) at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:271) at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1182) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1140) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1119) at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:271) at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1182) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1140) at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:271) at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65) at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56) at org.jboss.marshalling.MarshallerObjectOutputStream.writeObjectOverride(MarshallerObjectOutputStream.java:50) at org.jboss.marshalling.river.RiverObjectOutputStream.writeObjectOverride(RiverObjectOutputStream.java:179) at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:341) at java.base/java.util.HashMap.internalWriteEntries(HashMap.java:1858) at java.base/java.util.HashMap.writeObject(HashMap.java:1412) at org.jboss.marshalling.reflect.JDKSpecific$SerMethods.callWriteObject(JDKSpecific.java:134) at org.jboss.marshalling.reflect.SerializableClass.callWriteObject(SerializableClass.java:231) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1128) at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:271) at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1182) at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1140) at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:271) at org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:58) at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:116) at org.jenkinsci.plugins.workflow.support.pickles.serialization.RiverWriter.lambda$writeObject$1(RiverWriter.java:144) at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:331) at org.jenkinsci.plugins.workflow.support.pickles.serialization.RiverWriter.writeObject(RiverWriter.java:143) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgram(CpsThreadGroup.java:580) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgram(CpsThreadGroup.java:557) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.saveProgramIfPossible(CpsThreadGroup.java:540) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:464) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:331) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:295) at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:97) 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 jenkins.util.ErrorLoggingExecutorService.lambda$wrap$0(ErrorLoggingExecutorService.java:51) 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

Removing the readyaml command causes the error to disappear. This doesn't make any sense to me, why would it try to serialize the StringJoiner in the unrelated readyaml call?

Yaml contents

---
HELLO: "WORLD"

Solution

  • See documentation:

    Local variables are captured as part of the pipeline’s state during serialization. This means that storing non-serializable objects in variables during pipeline execution will result in a NotSerializableException to be thrown.

    readYaml and most other steps (but not all) triggers serialization. I guess it is a fundamental trade off - Jenkins pipeline execution can survive Jenkins restart, but to do that it asks you to only have serializable variables, so it can save and restore its state after the restart.

    Doesn't mean you cannot use any non-serializable classes, because you can, just don't save their instances to variables. This is no problem:

    env.out = new StringJoiner(",")
      .add("a").add("b").add("c")
      .toString()