Docs menu

Troubleshooting Scala

The build and runtime errors Scala apps hit most on Primcloud, with the fix for each.

On this page

Two places to look when a deploy misbehaves:

  • Build log: the Deployments tab streams the full build output, including sbt's own compile errors and stack traces.
  • Runtime log: the Logs tab streams stdout and stderr from your running pods. Startup crashes, unhandled exceptions, and JNI load failures show up here.

The errors below run roughly in the order they'd bite you: detection, version resolution, sbt configuration, the sbt build, then runtime.

Build errors#

No Scala application found#

Primcloud looked for *.sbt, project/build.properties, or a .scala file under project/ or .sbt/ at the repo root and found none.

Fix. Move build.sbt and the project/ directory to the repo root, or open a monorepo subdirectory as its own Primcloud app.

No sbt version defined#

project/build.properties is missing, or it exists but doesn't set sbt.version. Primcloud needs an explicit sbt version to run the build.

Fix. Create project/build.properties with:

project/build.properties
sbt.version=1.11.7

Use whatever 1.x or 2.x release your build was developed against. See Runtime versions for the supported range.

Unsupported sbt version#

The sbt.version is outside the 1.x and 2.x series, or it's exactly 2.0.0, which Primcloud rejects, because upstream sbt has a plugin-loading bug in that release.

Fix. Bump to a supported version: the latest 1.x for stability, or 2.0.1 or later on the 2.x line.

Unexpected version parse error#

The sbt.version in project/build.properties isn't a valid version, often a typo (1.11..7) or a build tag pasted in whole.

Fix. Use a plain MAJOR.MINOR.PATCH value. Run sbt --version locally to see what your project resolves to.

Unsupported OpenJDK version#

The java.runtime.version in system.properties doesn't match a version Primcloud can install: either an unrecognized major or a patch that isn't available.

Fix. Set it to a supported major (such as 17, 21, or 25) or a specific patch listed in Runtime versions. Pinning a major is usually safer than a patch, since new patches ship automatically.

stage task not found#

Your build.sbt doesn't define a stage task. Most Scala apps get one from sbt-native-packager, but a bare sbt project won't.

Fix. Enable sbt-native-packager:

project/plugins.sbt
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.7")
build.sbt
enablePlugins(JavaAppPackaging)

Or define a stage task yourself:

build.sbt
lazy val stage = taskKey[Unit]("Stage task")
stage := (Compile / compile).value

Or override the default tasks; see Customizing the build → Task and project overrides.

sbt build hit an out-of-memory error#

Large multi-project builds, heavy annotation-processor plugins, or a build that holds the whole dependency graph in memory can exhaust sbt's heap.

Fix. Give sbt a larger heap through SBT_OPTS:

Set in the Secrets panel:

SBT_OPTS=-J-Xmx2g -J-Xss4m

Could not parse list / Could not parse boolean#

The value for SBT_TASKS, SBT_PRE_TASKS, SBT_CLEAN, or SBT_AVAILABLE_AT_LAUNCH didn't parse.

Fix. For task lists, use plain space-separated names, with no quotes unless a name contains a space. For booleans, use exactly true or false.

Resolver returned 401 Unauthorized#

sbt reached your private Maven or Ivy repository but the credentials didn't authenticate.

Fix. See Package managers → Private packages and registries. The usual causes:

  • The Credentials(realm, host, user, pass) realm doesn't match the repo's WWW-Authenticate header.
  • The environment variables holding the credentials aren't set on the environment the build runs in.
  • The token has expired or been rotated.

Runtime errors#

App doesn't bind to $PORT#

Symptom: the deployment reaches Release Succeeded, but the URL returns a connection error and the runtime log has no "listening on" line.

Fix. Confirm the web process binds to $PORT. For Play, the standard Procfile line does it:

Procfile
web: target/universal/stage/bin/my-app -Dhttp.port=${PORT}

For Akka HTTP, http4s, and other frameworks, read PORT from the environment and pass it to the server binder:

Main.scala
val port = sys.env.getOrElse("PORT", "8080").toInt

A hardcoded port (like 9000) never sees traffic. Primcloud routes to $PORT on the web formation.

UnsatisfiedLinkError at runtime#

java.lang.UnsatisfiedLinkError: no libfoo in java.library.path

Your code, or a JNI-bound library, needs an OS-level shared library that isn't in the run image.

Fix. Install it as a Debian package; see Customizing the build → Debian packages. For libpq5, libmariadb-dev, and similar, add the package name to project.toml and redeploy. If you're unsure of the name, check it with apt search <library> in an Ubuntu 24.04 container.

App exits immediately on start#

The runtime log shows the process starting, then exiting non-zero with little else.

Most common causes:

  • An exception before the logger is up. Wrap main in a try/catch that writes the exception to System.err before rethrowing, redeploy, and read the stack trace in the runtime log.
  • A missing environment variable. If your app reads a database URL or API key and throws on null, set it in the Secrets panel and redeploy.
  • Wrong bin name in the Procfile. If the Procfile invokes target/universal/stage/bin/wrong-name, that file doesn't exist and the launcher exits at once. sbt-native-packager names the output from your name in build.sbt, lowercased, with spaces turned to hyphens.
  • Out of memory at startup. A large static initialization can use more memory than the compute size has, and an app that does is restarted. Move to a larger compute size.

Slow startup / cold-start timeouts#

Scala's JVM warmup runs longer than most languages, and Play compiles its routing table on the first request. If health checks time out on the first deploy after a quiet period:

Fix. Warm the JVM before it takes traffic by hitting an internal health-check endpoint from your entrypoint, or move to a larger compute size, since more CPU means faster JIT.

Debugging tips#

  • Reproduce locally with the same command. sbt compile stage on your machine mirrors what Primcloud runs, provided your local JDK matches system.properties. Most build failures reproduce this way.
  • Check the resolved versions. The build log prints exactly which sbt and JDK got installed. Compare them against project/build.properties and system.properties.
  • Turn on sbt debug logging. Set SBT_OPTS=-J-Dsbt.log.noformat=true -J-Dsbt.log.level=debug in the Secrets panel temporarily. The log gets noisier, but resolution and task-graph errors gain a lot of detail.
  • Inspect the staged output. Add a small sbt task that lists target/universal/stage/ after stage runs, and hook it in through SBT_PRE_TASKS. It costs a deploy but shows exactly what lands in the image and what your Procfile should invoke.
  • Expect one slow deploy after a version bump. Bumping sbt, Scala, or the JDK invalidates the matching cache. The next deploy is slower; the one after is fast again.