FAQ

Here are some of the questions about PureConfig frequently asked on issues and other channels:

How can I use PureConfig with Spark 2.1.0 (problematic shapeless dependency)?

Apache Spark (specifically version 2.1.0) has a transitive dependency on shapeless 2.0.0. This version is too old to be used by PureConfig, making your Spark project fail when using spark-submit. The solution is to shade, i.e. rename, the version of shapeless used by PureConfig.

SBT

If you are using the sbt-assembly plugin to create your JARs you can shade shapeless by adding to your assembly.sbt file the following setting:

assemblyShadeRules in assembly := Seq(ShadeRule.rename("shapeless.**" -> "new_shapeless.@1").inAll)

Maven

The maven-shade-plugin can shade shapeless by adding to your pom.xml file the following block:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.0.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <createDependencyReducedPom>false</createDependencyReducedPom>
        <relocations>
            <relocation>
                <pattern>shapeless</pattern>
                <shadedPattern>shapelesspureconfig</shadedPattern>
            </relocation>
        </relocations>
    </configuration>
</plugin>

How can I avoid displaying sensitive parts of my configuration, such as passwords, when I print the result of PureConfig?

Configuration commonly includes sensitive information, such as passwords or keys. Generally these values should not appear in logging.

Unfortunately this means that Scala’s typically helpful behavior of generating transparent toString methods gets in the way. When a configuration is loaded into a case class, then the default toString generated by Scala will display sensitive information. This behavior is standard Scala and has nothing to do with PureConfig per se.

A solution we’ve seen address this concern is to wrap the sensitive field in an AnyVal new type which simply redefines toString. Please see the example below:

import com.typesafe.config.ConfigFactory
import pureconfig._
import pureconfig.generic.auto._

case class Sensitive(value: String) extends AnyVal {
  override def toString: String = "MASKED"
}

case class SuperSecretConfig(
  username: String,
  password: Sensitive,
  apiKey: Sensitive)

Then when we print out the SuperSecretConfig after loading it via PureConfig, the sensitive values are masked:

val secret = ConfigSource.string("""{
  username: john.smith
  password: password123
  api-key: 8ef72f48-2143-48af-9573-3b519bbcb777
}""").loadOrThrow[SuperSecretConfig]
// secret: SuperSecretConfig = SuperSecretConfig(john.smith,MASKED,MASKED)

But, of course, the values we need are still there:

secret.password.value 
// res0: String = password123

secret.apiKey.value
// res1: String = 8ef72f48-2143-48af-9573-3b519bbcb777

How do I debug “implicit not found” errors?

When we want to load a config with a large structure and Scala refuses to compile a reader for it, it can be difficult to understand the reason of the failure. Consider the following example:

import com.typesafe.config.ConfigFactory
import pureconfig._
import pureconfig.generic.auto._

class Custom(x: Int, s: String) // not a case class
class Custom2(x: Int, s: String) // not a case class

sealed trait Conf
case class ConfA(a: Boolean, b: Option[Boolean]) extends Conf
sealed trait ConfB extends Conf
case class ConfB1(a: Int) extends ConfB
case class ConfB2(a: String) extends ConfB
case class ConfC(a: Option[Custom], b: Custom2) extends Conf

When we try to load a Conf from a config, we’ll simply get this error message:

ConfigSource.default.load[Conf]
// <console>:21: error: Cannot find an implicit instance of pureconfig.ConfigReader[Conf].
// If you are trying to read or write a case class or sealed trait consider using PureConfig's auto derivation by adding `import pureconfig.generic.auto._`
//        ConfigSource.default.load[Conf]
//                                 ^

In PureConfig, the derivation of config readers and writers is done by chaining implicits - the converters of larger structures (like Conf) depend on the implicit converters of smaller ones (like Boolean or Custom). However, Scala is not helpful in case one of those upstream dependencies is missing, limiting itself to showing the message above.

Since version 0.8.0, PureConfig provides a way to obtain better error messages for those cases. To enable it, we need to add the following entry to our SBT project:

scalacOptions += "-Xmacro-settings:materialize-derivations"

When code using PureConfig derived converters is compiled using the compiler flag above, an internal macro will analyze the dependencies between converters and show a much more helpful message in case of an error:

ConfigSource.default.load[Conf]
// <console>:18: error: could not derive a ConfigReader instance for type Conf, because:
//   - missing a ConfigReader instance for type ConfC, because:
//     - missing a ConfigReader instance for type Option[Custom], because:
//       - missing a ConfigReader instance for type Custom
//     - missing a ConfigReader instance for type Custom2
// 
//        ConfigSource.default.load[Conf]
//  

Since this is an experimental feature, it may not work as intended in some unforesseen cases. If you find any issue with it, please do open an issue!