config
config copied to clipboard
Some substitutions are resolved too early
I have created a minimal Scala 2.12 project with no dependencies and one test case that simply resolves the config files and prints the result:
class ConfigSpec extends FlatSpec with Matchers {
"The config" should "resolve" in {
val conf: Config = ConfigFactory.load()
println(conf.root().render(
ConfigRenderOptions.concise().setFormatted(true)
))
}
}
I found two (probably related) errors in which a substitution gets resolved too early. In both cases, I have an application.conf, but no reference.conf.
- With the following application.conf:
foo = ""
foo {
bar = ${override-me}
}
foo {
bar = "bar"
}
I get the following exception:
com.typesafe.config.ConfigException$UnresolvedSubstitution: application.conf @ file:<redacted>/target/scala-2.12/classes/application.conf: 3: Could not resolve substitution to a value: ${override-me}
I get the same exception if I change the first line to foo = null, foo = 0, etc. Notably, foo = {} works just fine. I thought of a "fix": if I add the following object after the first line, then the config resolves correctly:
foo {
bar = ""
}
- With the following application.conf, I get the same exception as above (with a different line number, of course):
empty = {}
foo = ${empty}
foo {
bar = ${override-me}
}
foo {
bar = "bar"
}
The "fix" from the first case doesn't seem to work here.
At first glance, I agree, this should not be trying to resolve (since it's overridden and there was nothing forcing it to resolve). The way I'd approach this bug would probably be to add a unit test similar to these: https://github.com/lightbend/config/blob/1fc2aff45226dfc863ab8a0e54c0ae946b2b30c8/config/src/test/scala/com/typesafe/config/impl/ConfigSubstitutionTest.scala#L320-L559 and then debug it.
I ran into this same issue so I went and wrote the test to debug it and the issue stems from the type disparity - foo is initially a string so when the parser encounters an unresolved object next, .withFallback goes to a mergeWithNonObject which creates a ConfigDelayedMerge which then in turn ends up trying to evaluate all three assignments to foo.
While finding the cause was relatively trivial, the fix seems far from simple - my initial impulse was to change the implementation of com.typesafe.config.impl.AbstractConfigObject#withFallback to ignore non-object value types (what does it even mean to merge an object with a list or a string?) but I didn't know the possible impacts of that decision so I thought I'd ask here first.
Okay so applying my changes makes the following test fail com.typesafe.config.impl.ConfigTest#mergeObjectThenSubstitutionThenObject, making the merge non-associative in case of mismatching types