scala.collection.SeqOps not found

#1

I am trying to upgrade to Scala 2.13, and I am having difficulties. I finally got everything to compile using sbt (with deprecation warnings that I will ignore for now). However, when I run, I get this:

java.lang.ClassNotFoundException: scala.collection.SeqOps

I don’t understand how my program can even compile if it can’t It can find all the classes that it needs. Any ideas why this is happening and what I should do about it? Thanks.

#2

I can’t be certain in this situation, but when I get run time errors like this it is generally because something in my project was compiled using an older version of Scala. Or perhaps I’m even trying to run it with an older version of Scala. Either way, a mismatch between the run time version and the compiler version can lead to error that report types not being found that are clearly available.

1 Like
#3

Yes, that occurred to me. I had one precompiled jar file in my lib directory which hadn’t been recompiled with 2.13, so I did so. I also updated my version of scala that is installed in my home directory independently of sbt. Now I am getting a different class file not found:

java.lang.ClassNotFoundException: scala.xml.MetaData

That’s progress I guess, but I’m still baffled. I am using XML, but the stack trace points to a line that doesn’t seem to have anything to do with XML.

#4

It looks like you’re missing scala xml on your classpath. This should all be solved automatically by whatever you’re using for dependency management – you can do it manually, of course, but then you’re going to hit these kinds of issues all the time.

The ClassNotFoundException is thrown when the classloader fails to load the class. It probably attempts to load the class at the point a class that references scala.xml.MetaData is initialized.

#5

I have the following in my build.sbt file:

libraryDependencies += “org.scala-lang.modules” %% “scala-xml” % “1.2.0”

Shouldn’t that be sufficient? (And I am using sbt 1.2.8)

#6

Could be another version skew: some transitive dependency that is relying on the 2.12 version of ScalaXML, and isn’t finding it in the 2.13 version. Are you seeing eviction warnings when you do a clean compile?

#7

I’m not sure what “eviction warnings” are, but I don’t think I’m seeing them. I’ve tried everything I can think of, including clean compiles and even deleting the entire target directory. I’ve verfied that I am using Scala 2.13 and sbt 1.2.8 consistently. What else can I try, short of reverting back to 2.12?

Had I know that upgrading to 2.13 would be so difficult, I would have waited several more months for the dust to settle.

#8

Can you share your build definition, so we can eyeball it for possible causes?

If you can’t, then perhaps you could attempt to minimize it to a minimal build definition that you could share, that still reproduces the problem. If you can isolate the issue, we’ll almost certainly spot the problem immediately. (As a bonus, there’s a good chance you would run into the solution yourself, as you minimize.)

(And, it’s hard to be sure, but it doesn’t sound to me like you’re hitting any issues that you wouldn’t have hit exactly the same if you’d waited a few months.)

#9

also asked at https://stackoverflow.com/questions/57503145/classnotfoundexception-in-scala-xml-with-scala-2-13, I see

#10

Yes, thanks. Here is my build.sbt file:

name := “trajspec”

version := “0.999”

organization := “gov.nasa.arc”

//scalaVersion := “2.12.8”
scalaVersion := “2.13.0”

crossPaths := false

unmanagedSourceDirectories in Test += baseDirectory.value / “src” / “plot”
unmanagedSourceDirectories in Test += baseDirectory.value / “src” / “sim”

scalaSource in Compile := baseDirectory.value / “src” / “main”
javaSource in Compile := baseDirectory.value / “src” / “main”
scalaSource in Test := baseDirectory.value / “src” / “test”
scalaSource in Test := baseDirectory.value / “src” / “plot”
scalaSource in Test := baseDirectory.value / “src” / “sim”
javaSource in Test := baseDirectory.value / “src” / “test”

//scalacOptions += “-Xdisable-assertions”

scalacOptions += “-deprecation”

scalacOptions += “-Xcheckinit”

//scalacOptions += “-Xfatal-warnings”

//scalacOptions += “-Xlint”

scalacOptions += “-feature”

scalacOptions += “-Ywarn-value-discard”

//scalacOptions += “-Ywarn-unused”

//scalacOptions += “-Ywarn-unused:imports”

scalacOptions += “-Ywarn-dead-code”

libraryDependencies +=
“org.scala-lang.modules” %% “scala-parallel-collections” % “0.2.0”

libraryDependencies += “org.scala-lang.modules” %% “scala-xml” % “1.2.0”

#11

That all seems normal, with the possible exception of crossPaths := false, which seem unusual to me. One should normally only ever set that to false on a Java-only project. It seems unlikely to be the culprit — at least, I have no theory about why it might be connected — but regardless, maybe try taking it out?

I don’t see anything else unusual in the build file.

#12

Also, how are you running your code exactly?

I ask because the error message seems to indicate that your classpath at runtime may differ from your classpath at compile time.

#13

Hey, I think you are on to something! I use a bash script to run. I wrote the script to simplify the necessary commands and to automatically archive the results for longer runs. The script sets its own CLASSPATH (to the project target/classes and target/test-classes directories), so that is likely to be the problem. I’m not sure why it worked for 2.12 for not 2.13, but I guess that doesn’t matter. How can I find out what the CLASSPATH is at compile time (or just set the CLASSPATH in my run scripts to be the same)? Thanks.

#14

I don’t use ScalaTest or any of those frameworks. Instead, I simply put each test in a separate scala file in my test directory. Each of those test files contains an object with a “main” function.

As I mentioned above, I use a bash script called “run” to execute a test. This script calls sbt to compile, then it runs the program using this line:

scala $project.test.$1 “${@:2}” | tee -a $1.out

This line just calls the test (name in $1) and passes along the command-line arguments. I invoke the run script from the directory containing the input data for the test.

The problem that I am having with ClassNotFoundException is apparently due to a difference between the compile-time CLASSPATH and the runtime CLASPATH.

Here’s my question. What command should I use in my run script to use sbt to run the test (including passing along the command-line arguments)?

I looked in the sbt manual, but I only seem to see how to run a test in ScalaTest or similar frameworks, which is not what I need to do. Thanks.

#15

in sbt, run export fullClasspath

I agree that this is a more maintainable approach than having to maintain a separate classpath in your script.

you want sbt runMain, with appropriate arguments.

note that if you want to pass arguments to runMain, you might think a shell invocation such as this would work:

sbt runMain my.main.Class arg1 arg2

but sbt will interpret this as four separate commands, not a single command with three arguments. so you need to pass the whole thing as a single shell argument:

sbt "runMain my.main.Class arg1 arg2"

getting the quoting right for constructing such an invocation on the fly in your shell script might be a bit tricky.

#16

Thanks. Since I upgraded from Scala 2.12 to 2.13, the scala-xml needs a new item on the CLASSPATH for some reason. If I manually add that to the CLASSPATH in my run script, it runs correctly. However, that requires a manual change whenever I upgrade scala-xml, which is less maintainable as you point out.

So I tried your suggestion for sbt runMain. When I run it, I get this:

[warn] No sbt.version set in project/build.properties, base directory: /home/rpaielli/trajspec/test/D10

It seems that sbt is supposed to be run from the top-level project directory, but the test is supposed to be run from a project subdirectory that contains the input data. What is the trick to running it from there?

#17

I see that sbt can provide the full classpath:

sbt “export fullClasspath”

So I got the brilliant idea of putting something like this in my bash run script:

CLASSPATH=`sbt “export fullClasspath”`

Note the backticks to run the command in bash. Only one problem: in addition to the classpath, sbt first prints out some startup information. Is there a way to suppress that startup information? Thanks.

#18

After some searching, I came up with this little gem:

CLASSPATH=`sbt --error ‘set showSuccess := false’ “export fullClasspath”`

Turns out I’m not the first person to ask for an sbt silent mode. It should be just a -s or -q option, but this little kludge is better than nothing I guess.