Scala java.lang.NoSuchMethodError: scala.Predef$.augmentString error

It's been a while since I've seen a problem related to using different versions of Scala, but I just ran into it now.

I have a weird setup on one computer, and what I think I did was to compile two libraries with Scala 2.10.x, while their build.sbt files indicated that the libraries were intended for Scala 2.9.1. This resulted in those files having names like scalaemail_2.9.1-1.0.jar and stockutils_2.9.1-1.0.jar, even though they were compiled with Scala 2.10.x and SBT 0.12.1. When I then tried to use those libraries with a new Scala script, which I executed with the Scala 2.10.x interpreter, I encountered this error message:

java.lang.NoSuchMethodError: scala.Predef$.augmentString(Ljava/lang/String;)Ljava/lang/String;
    at Main$$anon$1$GetStocks$$anonfun$main$2.apply(GetStocks.sh:42)
    at Main$$anon$1$GetStocks$$anonfun$main$2.apply(GetStocks.sh:41)
    at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:60)
    at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:44)
    at Main$$anon$1$GetStocks$.main(GetStocks.sh:41)
    at Main$$anon$1.<init>(GetStocks.sh:54)
    at Main$.main(GetStocks.sh:1)
    at Main.main(GetStocks.sh)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:78)
    at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:24)
    at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:88)
    at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:78)
    at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:33)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:40)
    (more lines omitted ...)

As mentioned, I got this error when attempting to run my Scala script, which begins with these lines:

#!/bin/sh
exec scala -classpath "lib/htmlcleaner-2.2.jar:lib/scalaemail_2.9.1-1.0.jar:lib/stockutils_2.9.1-1.0.jar" "$0" "$@"
!#

Suspecting that I was having a Scala version conflict problem of some type, I moved my script to a different computer that has only Scala 2.9.1, and also recompiled the libraries there, and everything started working as desired.

Honestly, I don't know what the magic clue is in this error message that should tell me that there's some sort of Scala version conflict happening. The NoSuchMethodError definitely told me something was wrong, but since this is the first time I've included jar files in a Scala script classpath, I thought that might be the problem. Perhaps the later ScalaClassLoader errors are also a symptom of the problem. As mentioned, moving my script and the source code for the libraries it depends on to a 2.9.1 system and then recompiling those libraries and running my script on that system solved this particular problem.

If you run into a Scala error message like this, I hope this brief post provides a few clues about the problem, and solution.

The entire Scala script

FWIW, as of tonight, my entire Scala script looks like this:

#!/bin/sh
exec scala -classpath "lib/htmlcleaner-2.2.jar:lib/scalaemail_2.9.1-1.0.jar:lib/stockutils_2.9.1-1.0.jar" "$0" "$@"
!#

import java.io._
import scala.io.Source
import com.devdaily.stocks.StockUtils
import scala.collection.mutable.ArrayBuffer

object GetStocks {

  case class Stock(symbol: String, name: String, price: BigDecimal)

  val DIR = System.getProperty("user.dir")
  val SLASH = System.getProperty("file.separator")
  val CANON_STOCKS_FILE = DIR + SLASH + "stocks.dat"
  val CANON_OUTPUT_FILE = DIR + SLASH + "quotes.out"
 
  def main(args: Array[String]) {

    // read the stocks file into a list of strings ("AAPL|Apple")
    val lines = Source.fromFile(CANON_STOCKS_FILE).getLines.toList
    
    // create a list of Stock from the symbol, name, and by retrieving the price
    var stocks = new ArrayBuffer[(Stock)]()
    lines.foreach{ line =>
      val fields = line.split("\\|")
      val symbol = fields(0)
      val html = StockUtils.getHtmlFromUrl(symbol)
      val price = StockUtils.extractPriceFromHtml(html, symbol)
      val stock = Stock(symbol, fields(1), BigDecimal(price))
      stocks += stock
    }
 
    // build a string we can output
    var sb = new StringBuilder
    stocks.foreach { stock =>
      sb.append("%s is %s\n".format(stock.name, stock.price))
    }
    val output = sb.toString
 
    // write the string to the file
    val pw = new PrintWriter(new File(CANON_OUTPUT_FILE))
    pw.write(output)
    pw.close

  }
}

GetStocks.main(args)

If you ever wanted to write a Scala script that uses import statements and case classes, I hope this is also a good example (if not a little rough around the edges at the moment).

Follow-up

This problem is discussed in detail in this December 7, 2011 article, "Scala's version fragility make the Enterprise argument near impossible".