Corda’s handling of Mutable Containers
Whilist developing a CorDapp in Corda, I noticed something interesting during my work.

In my CorDapp, I have a state, defined in a data class. One of the attributes of this state is a mutableMap. Mutable maps allow you to update its key-value pairs after initialization.
So I issued this state, and populated the mutableMap with 1 entry. So far, so good.
When I evolve the state, I do the following:
- Query for the input state
- Create the output state by using .copy method on input state
- Now I change stuff in this new output state depending on the flow (e.g. Changing the status from “REQUESTED” to “ACCEPTED”.. etc)
- In the new output state, I wanted to add another attribute to the map, so I fetched the map from this object and called the “put” method to add a new entry into the map.
- Build, sign, share the transaction.
Simple enough, right? I then ran a flow test to make sure everything works.
TEST FAILED.
[WARN ] [Node thread-1] flow.[ea6df23d-b3a9–423f-a5b9-b45a15640088].run - Terminated by unexpected exception {}
java.lang.UnsupportedOperationException: null
at java.util.Collections$UnmodifiableCollection.addAll(Unknown Source) ~[?:1.8.0_161]
……….
/// LONG STACK TRACE …..
I checked which line was throwing the exception — it was the place where I update the map. But “Collections$UnmodifiableCollection” ? How can a mutableMap give an UnmodifiableCollection? This left me puzzled for some time.
After doing some digging, I found the reason — Corda’s behavior when deserializing objects is to build them as immutable:
Java fundamentally provides no mechanism by which the mutability of a class can be determined, so this presents a problem for Corda’s serialization framework.
When reconstituting objects with container properties (lists, maps, etc) we must choose whether to create mutable or immutable objects. Given the restrictions, we have decided it is better to preserve the immutability of immutable objects rather than force mutability on presumed immutable objects.
A small example of this problem using mutableList
data class Foo(val l : MutableList<String>)// Corda internally runs the two statements below when you save to ledger and read back from itval bytes = Foo(mutableListOf ("a", "b", "c")).serialize()val newFoo = bytes.deserialize()newFoo.l.add("d") // UnsupportedOperationException
I’ve found that there are 3 ways to get around this:
- If using a data class, you can provide a “@ConstructorForDeserialization” annotation to tell Corda how to deserialize this object.
data class Foo(val l : MutableList<String>){@ConstructorForDeserialization@Suppress("Unused")constructor (l : Collection<String>) : this (l.toMutableList())}
2. Preserve immutability of objects (using Copy-on-Write semantics), then mutating the contents of the class can be done by creating a new copy of the data class with the altered list passed (in this example) passed in as the Constructor parameter.
data class Foo(val l : List<String>)// Corda internally runs the two statements below when you save to ledger and read back from itval bytes = Foo(listOf ("a", "b", "c")).serialize()val newFoo = bytes.deserialize()val newFoo = newFoo.copy (l = (newFoo.l + "d"))
3. After Corda deserializes the state for you, convert the list to a mutableList again by using list.toMutableList() method, then add to the list
data class Foo(val l : MutableList<String>)// Corda internally runs the two statements below when you save to ledger and read back from itval bytes = Foo(mutableListOf ("a", "b", "c")).serialize()val newFoo = bytes.deserialize()newFoo.l = newFoo.l.toMutableList()newFoo.l.add("d") // Works
Even if you didnt face this specific problem, hope this helped you to learn something new!
If you found this post helpful, give this article a clap and follow me on Twitter at _amolpednekar.