Skip to main content

How to Fix Common Scope and Wire Compile Errors

This guide is a lookup table for the errors that Scope, Resource.from and Wire report. Find your error message in the Quick Index, jump to its entry, and leave with a fix. Each entry shows the smallest code that triggers the error, the real compiler output (rendered by mdoc, not pasted), one or two sentences on why it happens, and the corrected code.

The entries cover:

  • the errors you will most likely hit first: escaping $[A] values, the $ receiver rule, dependency cycles, duplicate providers, and dependencies the macro cannot build;
  • every remaining code from E1 to E17, in code order;
  • the leak warning (W1) and the closed-scope runtime errors (RE1 to RE3);
  • the differences between the Scala 2 and Scala 3 macros.

The Problem​

Scope checks resource safety at compile time, and Resource.from resolves a whole dependency graph at compile time. That moves mistakes from production into the compiler, but it also means the first thing you see is a macro error. A message such as No given instance of type Unscoped[Scope.Child[Scope.global.type]#$[Db]] names the problem precisely, yet it doesn't tell you what to change. A large, unfamiliar error is the point where many people give up on a library.

Every Scope error has a known cause and a short fix. This page maps each message to both.

Prerequisites​

Add the module to your build:

libraryDependencies += "dev.zio" %% "zio-blocks-scope" % "0.0.56"

Every example on this page starts with this import:

import zio.blocks.scope.*

You should know what scoped, allocate, $ and Resource.from do. If you don't, read the Compile-Time Resource Safety with Scope tutorial first; this page doesn't re-explain those concepts.

Reading a Scope Error​

Most macro errors come in a box that starts with ── Scope Error ── and ends with a rule line. The first line inside the box says what went wrong, and the Hint: or Fix: line says what to do.

The codes on this page (E1, E10, RE2 and so on) come from the library source, ErrorMessages.scala. The compiler output doesn't print them, so match on the first line of the message, not on a code.

Three more things to know before you search:

  • Colors. In a terminal the boxes are colored. Set the NO_COLOR environment variable to any non-empty value, or set the system property -Dsbt.log.noformat=true, to get plain text. The check runs inside the compiler's JVM.
  • Type names in this page. mdoc compiles each snippet inside a generated class, so types appear as MdocApp3.this.Catalog. In your project you will see your own package, such as com.example.Catalog.
  • Some errors have no code. Escaping a scope and breaking the $ receiver rule come from the compiler's implicit search and from the $ macro, not from ErrorMessages. They are listed first because they are the most common.

Quick Index​

CodeThe message starts withUsual fix
noneNo given instance of type zio.blocks.scope.Unscoped[...]Return plain data, not the scoped value
noneParameter 1 ('d') must only be used as a method receiverCall a method on the parameter
nonevalue ... is not a member of scope.$[...]Go through $
E10(today: Exception occurred while executing macro expansion with key not found)Break the cycle
E11Multiple providers for ...Pass exactly one wire per type
E8Cannot auto-create ... / This type is abstractWire a concrete implementation
E7Cannot auto-create ... / This type (primitive, collection, or function)Provide the value with Wire(value)
E9Cannot auto-create ... / This type has no primary constructorProvide an explicit wire
E4Resource.from[...] cannot be derived.Pass wires, or call Resource.from[T]()
E1Cannot derive Wire for ...: not a class.Derive the concrete class
E2... has no primary constructor.Write the wire by hand
E3Dependency type conflict in ...Wrap one of the types
E5Unsupported implicit parameter in ...Make the parameter explicit
E6Cannot extract types from wire expression.Pass a real wire
E12Constructor of ... has multiple parameters of type ...Wrap one of the parameters
E13Expected varargs of Wire[?, ?] expressions.List the wires inline
E14-E17Cannot derive Unscoped for ... (Scala 2 only)Fix the type or write the instance
W1── Scope Warning ── / ... is being leaked from scopeStay inside $, or add Unscoped
RE1IllegalStateException: Cannot allocate resource: scope is already closed.Allocate while the scope is open
RE2IllegalStateException: Cannot open child scope: scope is already closed.Open while the parent is open
RE3IllegalStateException: Cannot access scoped value: scope is already closed.Use $ inside the owning scope

The Most Common Errors​

Returning a Scoped Value from scoped​

A scoped block may return a value only if its type has an Unscoped instance. A resource you allocated has type $[Db], which has none:

import zio.blocks.scope.*

class Db extends AutoCloseable {
def countBooks(): Int = 42
def close(): Unit = ()
}
val db = Scope.global.scoped { scope =>
import scope.*
allocate(Resource(new Db))
}
// error:
// No given instance of type zio.blocks.scope.Unscoped[
// zio.blocks.scope.Scope.Child[zio.blocks.scope.Scope.global.type]#$[
// MdocApp0.this.Db]
// ] was found for parameter ev of method scoped in trait ScopeVersionSpecific.
// I found:
//
// zio.blocks.scope.Unscoped.derived[
// zio.blocks.scope.Scope.Child[zio.blocks.scope.Scope.global.type]#$[
// MdocApp0.this.Db]
// ](
// /* missing */
// summon[
// scala.deriving.Mirror.Of[
// zio.blocks.scope.Scope.Child[zio.blocks.scope.Scope.global.type]#$[
// MdocApp0.this.Db]
// ]
// ]
// )
//
// But Failed to synthesize an instance of type scala.deriving.Mirror.Of[
// zio.blocks.scope.Scope.Child[zio.blocks.scope.Scope.global.type]#$[
// MdocApp0.this.Db]
// ]:
// * class Any is not a generic product because it is not a case class
// * class Any is not a generic sum because it is not a sealed class.
// }
// ^

The block's resources are closed when it exits, so returning db would hand out a closed connection. The Mirror lines in the message are the compiler explaining that it also tried, and failed, to derive an instance.

Return plain data computed from the resource instead:

val count: Int = Scope.global.scoped { scope =>
import scope.*
val db = allocate(Resource(new Db))
$(db)(_.countBooks())
}
// count: Int = 42
println(count)
// 42

The same error appears when you return your own type and it has no Unscoped instance:

import zio.blocks.scope.*

class Book(val title: String)
val book = Scope.global.scoped { _ => new Book("Zionomicon") }
// error:
// No given instance of type zio.blocks.scope.Unscoped[MdocApp1.this.Book] was found for parameter ev of method scoped in trait ScopeVersionSpecific.
// I found:
//
// zio.blocks.scope.Unscoped.derived[MdocApp1.this.Book](
// /* missing */summon[scala.deriving.Mirror.Of[MdocApp1.this.Book]])
//
// But Failed to synthesize an instance of type scala.deriving.Mirror.Of[MdocApp1.this.Book]:
// * class Book is not a generic product because it is not a case class
// * class Book is not a generic sum because it is not a sealed class.
// val book = Scope.global.scoped { _ => new Book("Zionomicon") }
// ^

Unscoped instances exist for primitives, String, collections, Option, Either and tuples of up to four elements, as long as every element type is itself Unscoped. For your own data, declare an instance. In Scala 3, case classes, sealed traits and enums whose fields are all Unscoped get one automatically. In Scala 2 you derive it explicitly:

import zio.blocks.scope._

final case class Book(title: String)
object Book {
implicit val unscoped: Unscoped[Book] = Unscoped.derived[Book]
}

val book: Book = Scope.global.scoped { _ => Book("Zionomicon") }

For a class that isn't a case class, write the instance by hand: given Unscoped[Book] = new Unscoped[Book] {} in Scala 3, or implicit val unscopedBook: Unscoped[Book] = new Unscoped[Book] {} in Scala 2. Only do this for pure data, never for a type that holds a resource.

If you return a case class that has a resource field, the error names the field's type, not the case class:

import zio.blocks.scope.*

class Db extends AutoCloseable { def close(): Unit = () }
case class Receipt(db: Db)
val receipt = Scope.global.scoped { _ => Receipt(new Db) }
// error:
// No given instance of type zio.blocks.scope.Unscoped[MdocApp3.this.Db] was found.
// I found:
//
// zio.blocks.scope.Unscoped.derived[MdocApp3.this.Db](
// /* missing */summon[scala.deriving.Mirror.Of[MdocApp3.this.Db]])
//
// But Failed to synthesize an instance of type scala.deriving.Mirror.Of[MdocApp3.this.Db]:
// * class Db is not a generic product because it is not a case class
// * class Db is not a generic sum because it is not a sealed class.

Derivation needs an Unscoped instance for every field, and Db has none. Keep the resource out of the returned type: put only plain data in Receipt.

Using a $ Parameter as a Value​

Inside $(db)(d => ...), the parameter d may only be the receiver of a method call. Returning it, storing it, passing it to a function or capturing it in a closure is rejected:

import zio.blocks.scope.*

class Db extends AutoCloseable {
def countBooks(): Int = 42
def close(): Unit = ()
}

def describe(db: Db): String = s"${db.countBooks()} books"
Scope.global.scoped { scope =>
import scope.*
val db = allocate(Resource(new Db))
$(db)(d => d)
()
}
// error:
// Parameter 1 ('d') must only be used as a method receiver. It cannot be returned, stored, passed as an argument, or captured. Scoped values may only be used as a method receiver (e.g., d.method()).
// $(db)(d => d)
// ^
Scope.global.scoped { scope =>
import scope.*
val db = allocate(Resource(new Db))
$(db)(describe)
}
// error:
// Parameter 1 ('db') cannot be passed as an argument to a function or method. Scoped values may only be used as a method receiver (e.g., db.method()).

Any of those uses would let the raw resource outlive the $ call, and with it the scope. Passing a method reference such as describe counts as passing the parameter itself as an argument.

Call methods on the parameter, and pass their results along instead:

def describeCount(count: Int): String = s"$count books"

Scope.global.scoped { scope =>
import scope.*
val db = allocate(Resource(new Db))
$(db)(d => describeCount(d.countBooks()))
}

The Scope reference lists every form the $ macro rejects.

Calling a Method Directly on $[A]​

A value of type $[Db] has none of Db's methods:

Scope.global.scoped { scope =>
import scope.*
val db = allocate(Resource(new Db))
db.countBooks()
()
}
// error:
// value countBooks is not a member of scope.$[MdocApp4.this.Db]

$[A] is an opaque wrapper, so the compiler can see when you use the resource. Go through $:

Scope.global.scoped { scope =>
import scope.*
val db = allocate(Resource(new Db))
$(db)(_.countBooks())
}

E10: Dependency Cycle Detected​

Two services that need each other can't both be built first:

import zio.blocks.scope.*

class InventoryRepo(prices: PriceService)
class PriceService(inventory: InventoryRepo)
class Catalog(inventory: InventoryRepo)
Resource.from[Catalog]()

ErrorMessages defines an E10 message with a cycle diagram, but in the current release Resource.from never reaches it. A cycle crashes the macro with Exception occurred while executing macro expansion. followed by java.util.NoSuchElementException: key not found: and the name of a type in the cycle, plus a long stack trace (zio-blocks#1767). If you see that exception from Resource.from, look for a cycle.

Break the cycle by restructuring the dependencies. Here both services only needed shared data, so they now depend on a Db instead of on each other:

import zio.blocks.scope.*

class Db
class PriceService(db: Db)
class InventoryRepo(db: Db, prices: PriceService)
class Catalog(inventory: InventoryRepo)
Resource.from[Catalog]()

You can also break a cycle by putting a trait between the two classes and wiring an implementation that doesn't depend back on the first one.

E11: Multiple Providers for a Type​

Resource.from matches each dependency against the wires you pass, subtypes included. Two wires that both provide PriceService are ambiguous:

import zio.blocks.scope.*

trait PriceService
class FixedPrices extends PriceService
class DiscountPrices extends PriceService
class Catalog(prices: PriceService)
Resource.from[Catalog](Wire.shared[FixedPrices], Wire.shared[DiscountPrices])
// error:
// ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Multiple providers for MdocApp7.this.PriceService
//
// Conflicting wires:
// 1. MdocApp7.this.FixedPrices
// 2. MdocApp7.this.DiscountPrices
//
// Hint: Remove duplicate wires or use distinct wrapper types.
//
// ────────────────────────────────────────────────────────────────────────────────

The macro can't guess which implementation Catalog should get. Pass exactly one wire for each dependency type:

Resource.from[Catalog](Wire.shared[FixedPrices])

If you really need both implementations in one graph, give them distinct types, for example wrapper case classes, and make the constructors ask for those types.

E8: Abstract Type Cannot Be Auto-Created​

Resource.from builds every dependency you don't provide a wire for, but it can't instantiate a trait or an abstract class:

import zio.blocks.scope.*

trait PriceService
class FixedPrices extends PriceService
class Catalog(prices: PriceService)
Resource.from[Catalog]()
// error:
// ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Cannot auto-create MdocApp8.this.PriceService
//
// This type is abstract (trait or abstract class).
//
// Required by:
// └── MdocApp8.this.Catalog
//
// Fix: Provide a wire for a concrete implementation:
//
// Resource.from[...](
// Wire.shared[ConcreteImpl], // provides MdocApp8.this.PriceService
// ...
// )
//
// ────────────────────────────────────────────────────────────────────────────────

The Required by: tree shows which constructor asked for the abstract type. Wire a concrete implementation:

Resource.from[Catalog](Wire.shared[FixedPrices])

E7: Primitive, Collection, or Function Cannot Be Auto-Created​

The macro never invents values for String, the numeric types, Boolean, Unit, List, Seq, Set, Map, Option or function types:

import zio.blocks.scope.*

class Db(url: String)
class Catalog(db: Db)
Resource.from[Catalog]()
// error:
// ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Cannot auto-create java.lang.String
//
// This type (primitive, collection, or function) cannot be auto-created.
//
// Required by:
// ├── MdocApp9.this.Db
// └── MdocApp9.this.Catalog
//
// Fix: Provide Wire(value) with the desired value:
//
// Resource.from[...](
// Wire(...), // provide a value for java.lang.String
// ...
// )
//
// ────────────────────────────────────────────────────────────────────────────────

There is no sensible default for a database URL. Provide the value with Wire(value):

Resource.from[Catalog](Wire("jdbc:h2:mem:bookstore"))

If a graph needs two values of the same primitive type, wrap each in its own case class (for example final case class DbUrl(value: String)), because the graph has one slot per type.

E9: No Constructor for Auto-Create​

This message means a dependency is a concrete class with no primary constructor. Every Scala class has one, so this guide found no way to trigger E9 from Scala code. If you do see it, provide an explicit wire for that type, as the message's Fix: line shows.

E4: Resource.from[T] Has Dependencies​

Resource.from[T] without an argument list only works for types whose constructor takes nothing, or takes only a Finalizer or Scope:

import zio.blocks.scope.*

class Db extends AutoCloseable { def close(): Unit = () }
class Catalog(db: Db)
Resource.from[Catalog]
// error:
// ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Resource.from[MdocApp10.this.Catalog] cannot be derived.
//
// MdocApp10.this.Catalog has dependencies that must be provided:
// • MdocApp10.this.Db
//
// Hint: Use Resource.from[MdocApp10.this.Catalog](wire1, wire2, ...)
// to provide wires for all dependencies.
//
// ────────────────────────────────────────────────────────────────────────────────

That form doesn't resolve a dependency graph. Use the wires form: pass wires for the dependencies, or an empty argument list to let the macro build every dependency it can:

val explicitDb = Resource.from[Catalog](Wire.shared[Db])
val autoDb = Resource.from[Catalog]()

Note the difference between Resource.from[Catalog] and Resource.from[Catalog](): only the second one resolves dependencies.

The Remaining Compile Errors​

E1: Not a Class​

Wire.shared[T], Wire.unique[T] and Resource.from[T] call T's constructor, so T must be a concrete class:

import zio.blocks.scope.*

trait PriceService
class FixedPrices extends PriceService
Wire.shared[PriceService]
// error:
// ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Cannot derive Wire for MdocApp11.this.PriceService: not a class.
//
// Hint: Use Wire.Shared / Wire.Unique directly.
//
// ────────────────────────────────────────────────────────────────────────────────

A trait or an abstract class has no constructor to call. Derive the concrete class; the wire still provides PriceService to anything that needs one:

val prices = Wire.shared[FixedPrices]

If you need construction logic the macro can't derive, build the wire by hand with Wire.Shared or Wire.Unique, as the hint says.

E2: No Primary Constructor​

Like E9, this message needs a class without a primary constructor, and every Scala class has one, so this guide found no Scala trigger for it. The fix is to write the wire by hand with Wire.Shared or Wire.Unique.

E3: Subtype Conflict Between Dependencies​

A constructor can't take two dependencies where one type is a subtype of the other:

import zio.blocks.scope.*

class InventoryRepo
class SqlInventoryRepo extends InventoryRepo
class Catalog(inventory: InventoryRepo, sql: SqlInventoryRepo)
Wire.shared[Catalog]
// error:
// ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Dependency type conflict in MdocApp12.this.Catalog
//
// MdocApp12.this.SqlInventoryRepo is a subtype of MdocApp12.this.InventoryRepo.
//
// When both types are dependencies, Context cannot reliably distinguish
// them. The more specific type may be retrieved when the more general
// type is requested.
//
// To fix this, wrap one or both types in a distinct wrapper:
//
// case class WrappedMdocApp12.this.InventoryRepo(value: MdocApp12.this.InventoryRepo)
// or
// opaque type WrappedMdocApp12.this.InventoryRepo = MdocApp12.this.InventoryRepo
//
// ────────────────────────────────────────────────────────────────────────────────

The dependency graph is indexed by type, so a request for InventoryRepo could be answered with the SqlInventoryRepo. Wrap one of the two types so they're unrelated. The code in the message's hint isn't valid Scala when the type has a package prefix, so write the wrapper yourself:

import zio.blocks.scope.*

class InventoryRepo
class SqlInventoryRepo extends InventoryRepo
final case class ReadReplica(repo: SqlInventoryRepo)
class Catalog(inventory: InventoryRepo, replica: ReadReplica)
val catalog = Wire.shared[Catalog]

E5: Unsupported Implicit Parameter​

This message targets a Resource.from[T] constructor with an implicit parameter other than Finalizer or Scope. Both macros check for dependencies (E4) first, and an implicit Clock parameter is reported as an E4 dependency, so E5 isn't reachable from user code. Follow the E4 fix: make the parameter explicit and provide it with a wire.

E6: Cannot Extract Wire Types​

Every real wire has the type Wire[In, Out], so Resource.from reports E6 only when an argument has some other static type, such as ??? (type Nothing). Pass real Wire values.

E12: Duplicate Parameter Type​

This message is for a constructor with two parameters of the same type. In Scala 3 it isn't reported: the code compiles, and both parameters receive the same instance:

import zio.blocks.scope.*

class Db
class Catalog(val primary: Db, val replica: Db)
val sameDb: Boolean = Scope.global.scoped { scope =>
import scope.*
val catalog = allocate(Resource.from[Catalog]())
$(catalog)(c => c.primary.eq(c.replica))
}
// sameDb: Boolean = true
println(sameDb)
// true

The graph holds one value per type, so it can't supply two different Db values. If the two parameters must differ, give each a distinct type:

import zio.blocks.scope.*

class Db
final case class PrimaryDb(db: Db)
final case class ReplicaDb(db: Db)
class Catalog(primary: PrimaryDb, replica: ReplicaDb)
Resource.from[Catalog](
Wire(PrimaryDb(new Db)),
Wire(ReplicaDb(new Db))
)

E13: Invalid Varargs​

Resource.from[T](wires*) reads its arguments at compile time, so it needs them written out at the call site:

import zio.blocks.scope.*

class Db
class Catalog(db: Db)
val wires = Seq(Wire.shared[Db])
Resource.from[Catalog](wires*)
// error:
// ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Expected varargs of Wire[?, ?] expressions.
//
// Got: MdocApp16.this.wires: _*
//
// Hint: Use Resource.from[T](wire1, wire2, ...)
//
// ────────────────────────────────────────────────────────────────────────────────
// Resource.from[Catalog](wires*)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A Seq only exists at run time, so the macro can't see which wires it holds. List the wires inline:

Resource.from[Catalog](Wire.shared[Db])

E13 is Scala 3 only.

E14 to E17: Unscoped Derivation Errors (Scala 2 Only)​

These four errors come from Scala 2's Unscoped.derived[A] macro, which you call yourself (see the Scala 2 tab in Returning a Scoped Value). All of them start with Cannot derive Unscoped for:

  • E14, type parameter not deducible. A subtype of a sealed generic type has a type parameter the macro can't work out from the parent's type arguments. Give the subtype concrete type arguments, or simplify the hierarchy.
  • E15, sealed type with no known subclasses. Define at least one subclass in the same file as the sealed type.
  • E16, no Unscoped instance for a field or subtype. For example, Unscoped.derived[Receipt] where Receipt has a Db field. Remove the resource from the type, or give the field's type its own instance if it's pure data.
  • E17, no primary constructor. Write the instance by hand: implicit val unscoped: Unscoped[A] = new Unscoped[A] {}.

Scala 3 doesn't use these codes. Its Unscoped derivation is a given built on Mirror, so the same mistakes produce the compiler's standard No given instance of type ...Unscoped[...] message, as in Returning a Scoped Value.

Warnings​

W1: Value Leaked from Its Scope​

leak unwraps a $[A] into a plain A, which turns off the compile-time checks for that value. The macro warns every time:

import zio.blocks.scope.*

class Db extends AutoCloseable {
def countBooks(): Int = 42
def close(): Unit = ()
}
Scope.global.scoped { scope =>
import scope.*
val db = allocate(Resource(new Db))
val raw: Db = leak(db)
()
}
// warn:
// ── Scope Warning ───────────────────────────────────────────────────────────────
//
// leak(db)
// ^
// |
//
// Warning: db is being leaked from scope zio.blocks.scope.Scope.Child[zio.blocks.scope.Scope.global].
// This may result in undefined behavior.
//
// Hint:
// If you know this data type is not resourceful, then add an Unscoped
// instance for it so you do not need to leak it.
//
// ────────────────────────────────────────────────────────────────────────────────
// val raw: Db = leak(db)
// ^^

After the block exits, raw points at a closed resource. Keep the work inside $:

Scope.global.scoped { scope =>
import scope.*
val db = allocate(Resource(new Db))
$(db)(_.countBooks())
}

Use leak only to hand a resource to a library that can't accept $[A], and keep that library call inside the block. If the leaked type is pure data, give it an Unscoped instance instead: $ then returns it unwrapped and nothing has to leak.

Runtime Errors​

These errors are IllegalStateExceptions, thrown when code uses a scope after it has closed. The compiler can't catch them when a scope reference outlives its block, for example through open() and close(), a field, or another thread.

RE1: Allocate on a Closed Scope​

val shop1 = Scope.global.open()
val closed1 = shop1.scope
shop1.close()
closed1.allocate(Resource(new Db))
// java.lang.IllegalStateException: ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Cannot allocate resource: scope is already closed.
//
// Scope: Scope.Child
//
// What happened:
// A call to allocate was made on a scope whose finalizers have
// already run. The resource was never acquired.
//
// Common causes:
// • A scope reference escaped a scoped { } block (e.g. stored in a
// field, captured in a Future or passed to another thread).
// • close() was called on an OpenScope before all
// allocations inside it completed.
//
// Fix:
// Call allocate only inside a live scoped { } block, or before
// calling close() on an OpenScope.
//
// // Correct usage:
// Scope.global.scoped { scope =>
// import scope.*
// val db = allocate(Resource(new Database))
// $(db)(_.query("SELECT 1"))
// }
//
// ────────────────────────────────────────────────────────────────────────────────
// at zio.blocks.scope.Scope.allocate(Scope.scala:173)
// at repl.MdocSession$MdocApp43.$init$$$anonfun$8(fix-scope-and-wire-compile-errors.md:553)

close() already ran the scope's finalizers, so a new resource would never be released. Allocate before you close the scope.

RE2: Open a Child of a Closed Scope​

val shop2 = Scope.global.open()
val closed2 = shop2.scope
shop2.close()
closed2.open()
// java.lang.IllegalStateException: ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Cannot open child scope: scope is already closed.
//
// Scope: Scope.Child
//
// What happened:
// A call to open() was made on a scope whose finalizers have
// already run. No child scope was created.
//
// Common causes:
// • A scope reference escaped a scoped { } block and open()
// was called after the block exited.
// • close() was called on the parent OpenScope before
// open() was called on it.
//
// Fix:
// Call open() only on a live (not yet closed) scope.
//
// // Correct usage:
// Scope.global.scoped { scope =>
// import scope.*
// val child = open()
// $(child)(_.scope.allocate(Resource(new Database)))
// }
//
// ────────────────────────────────────────────────────────────────────────────────
// at zio.blocks.scope.Scope.open(Scope.scala:232)
// at repl.MdocSession$MdocApp43.$init$$$anonfun$9(fix-scope-and-wire-compile-errors.md:565)

A child scope is linked to its parent's finalizers, and a closed parent has none left to link to. Open children while the parent is still open.

RE3: Use $ on a Closed Scope​

val shop3 = Scope.global.open()
val closed3 = shop3.scope
val db3 = closed3.allocate(Resource(new Db))
shop3.close()
(closed3 $ db3)(_.countBooks())
// java.lang.IllegalStateException: ── Scope Error ─────────────────────────────────────────────────────────────────
//
// Cannot access scoped value: scope is already closed.
//
// Scope: Scope.Child
//
// What happened:
// The $ operator was called on a scope whose finalizers have
// already run. The underlying resource may have been released.
// Accessing it would be undefined behavior.
//
// Common causes:
// • A $[A] value or its owning scope escaped a scoped { }
// block (e.g. captured in a Future, stored in a field, or
// passed to another thread).
// • close() was called on an OpenScope that still has
// live $[A] values being accessed.
//
// Fix:
// Ensure all $ calls occur strictly within the scoped { }
// block that owns the value, and that the scope has not been closed.
//
// // Correct usage:
// Scope.global.scoped { scope =>
// import scope.*
// val db = allocate(Resource(new Database))
// $(db)(_.query("SELECT 1")) // $ used inside the block
// }
//
// ────────────────────────────────────────────────────────────────────────────────
// at repl.MdocSession$MdocApp43.$init$$$anonfun$10(fix-scope-and-wire-compile-errors.md:578)

The value behind db has already been released. Make every $ call before the owning scope closes.

defer on a closed scope is not an error: the finalizer is ignored and you get a no-op handle back. Calling scoped from a thread that doesn't own the scope throws a different IllegalStateException, Cannot create child scope: current thread '...' does not own this scope. The tutorial's Thread Ownership section covers that case.

Scala 2 and Scala 3 Differences​

Both macro implementations render their errors with the same ErrorMessages functions, so the boxes read the same in both versions. The differences:

  • E13 is reported only by Scala 3.
  • E14 to E17 are reported only by Scala 2, by Unscoped.derived. Scala 3 reports the same mistakes with the compiler's standard No given instance message.
  • Escaping a scope is a missing implicit in both versions, but Scala 2 uses its own wording for a missing implicit.
  • Type names are printed with toString in Scala 2 and with show in Scala 3, so some types are formatted slightly differently.

mdoc compiles this page with Scala 3, so every rendered message above is Scala 3 output.

Putting It Together​

This bookstore graph compiles cleanly. It provides the database URL with Wire(value) (E7), wires a concrete PriceService (E8), avoids a cycle between InventoryRepo and PriceService (E10), and returns a case class instead of a scoped value:

scope-examples/src/main/scala/scope/examples/compileerrors/BookstoreWiringExample.scala
package scope.examples.compileerrors

import zio.blocks.scope._

/**
* A bookstore service graph that compiles cleanly, built from the fixes in the
* "How to Fix Common Scope and Wire Compile Errors" guide:
*
* - `Db` needs a `String`, which cannot be auto-created, so the graph
* provides it with `Wire(value)`.
* - `PriceService` is a trait, so the graph names a concrete implementation
* with `Wire.shared[FixedPrices]`.
* - `InventoryRepo` and `PriceService` both depend on `Db` instead of on each
* other, so there is no dependency cycle.
* - The `scoped` block returns a `Quote` case class, which has an `Unscoped`
* instance, instead of the scoped `Catalog` itself.
*/

final class Db(url: String) extends AutoCloseable {
println(s"[Db] connected to $url")

private val stock = Map("Zionomicon" -> 3, "Programming in Scala" -> 0)
private val price = Map("Zionomicon" -> BigDecimal("49.00"), "Programming in Scala" -> BigDecimal("59.95"))

def stockOf(title: String): Int = stock.getOrElse(title, 0)
def priceOf(title: String): BigDecimal = price.getOrElse(title, BigDecimal(0))

def close(): Unit = println("[Db] closed")
}

trait PriceService {
def priceOf(title: String): BigDecimal
}

final class FixedPrices(db: Db) extends PriceService {
def priceOf(title: String): BigDecimal = db.priceOf(title)
}

final class InventoryRepo(db: Db) {
def stockOf(title: String): Int = db.stockOf(title)
}

final class Catalog(inventory: InventoryRepo, prices: PriceService) {
def quote(title: String): Quote = Quote(title, prices.priceOf(title), inventory.stockOf(title))
}

final case class Quote(title: String, price: BigDecimal, inStock: Int)

@main def bookstoreWiringExample(): Unit = {
val quote: Quote = Scope.global.scoped { scope =>
import scope._

val catalog = allocate(
Resource.from[Catalog](
Wire("jdbc:h2:mem:bookstore"), // String cannot be auto-created: provide the value
Wire.shared[FixedPrices] // PriceService is a trait: pick an implementation
)
)

$(catalog)(_.quote("Zionomicon"))
}

println(quote)
}

Running the Examples​

Clone the repository and move into the examples module:

git clone https://github.com/zio/zio-blocks.git
cd zio-blocks/scope-examples
A bookstore graph with every fix applied
scope-examples/src/main/scala/scope/examples/compileerrors/BookstoreWiringExample.scala
package scope.examples.compileerrors

import zio.blocks.scope._

/**
* A bookstore service graph that compiles cleanly, built from the fixes in the
* "How to Fix Common Scope and Wire Compile Errors" guide:
*
* - `Db` needs a `String`, which cannot be auto-created, so the graph
* provides it with `Wire(value)`.
* - `PriceService` is a trait, so the graph names a concrete implementation
* with `Wire.shared[FixedPrices]`.
* - `InventoryRepo` and `PriceService` both depend on `Db` instead of on each
* other, so there is no dependency cycle.
* - The `scoped` block returns a `Quote` case class, which has an `Unscoped`
* instance, instead of the scoped `Catalog` itself.
*/

final class Db(url: String) extends AutoCloseable {
println(s"[Db] connected to $url")

private val stock = Map("Zionomicon" -> 3, "Programming in Scala" -> 0)
private val price = Map("Zionomicon" -> BigDecimal("49.00"), "Programming in Scala" -> BigDecimal("59.95"))

def stockOf(title: String): Int = stock.getOrElse(title, 0)
def priceOf(title: String): BigDecimal = price.getOrElse(title, BigDecimal(0))

def close(): Unit = println("[Db] closed")
}

trait PriceService {
def priceOf(title: String): BigDecimal
}

final class FixedPrices(db: Db) extends PriceService {
def priceOf(title: String): BigDecimal = db.priceOf(title)
}

final class InventoryRepo(db: Db) {
def stockOf(title: String): Int = db.stockOf(title)
}

final class Catalog(inventory: InventoryRepo, prices: PriceService) {
def quote(title: String): Quote = Quote(title, prices.priceOf(title), inventory.stockOf(title))
}

final case class Quote(title: String, price: BigDecimal, inStock: Int)

@main def bookstoreWiringExample(): Unit = {
val quote: Quote = Scope.global.scoped { scope =>
import scope._

val catalog = allocate(
Resource.from[Catalog](
Wire("jdbc:h2:mem:bookstore"), // String cannot be auto-created: provide the value
Wire.shared[FixedPrices] // PriceService is a trait: pick an implementation
)
)

$(catalog)(_.quote("Zionomicon"))
}

println(quote)
}

Run it with:

sbt "runMain scope.examples.compileerrors.bookstoreWiringExample"

To compile the example without running it:

sbt compile

See Also​