What happens when a closeable resource is never closed?

← Error Handling and Exceptions · Ref: Q803

EK9 raises E81056 when a variable with a close() operator is created but never closed and does not escape the current scope. This is a guaranteed resource leak.

WHAT COUNTS AS ESCAPE

A resource escapes (ownership transferred) when:
- RETURNED: assigned to return variable
- STORED: assigned to a field or collection
- PASSED: given to another method as argument
- WRAPPED: placed in Optional, Result, or container

FIX OPTIONS

1. try-with-resources: automatic close when scope ends (recommended)
2. Return it: transfer ownership to the caller
3. Pass it: give to another component for management
4. Store it: place in a container for batch management

Research shows ~30% of resource leak bugs are caused by developers forgetting to close resources entirely.

See Q802 for manual close rejection. See Q137 for try-with-resources syntax.

Example

defines module qa.errorhandling.resourceneverclosed

  defines class

    FileConn
      fileName <- String()

      FileConn()
        -> name as String
        fileName: name

      readAll()
        <- content as String: "contents of " + fileName

      operator close as pure
        require true

      override operator ? as pure
        <- rtn <- true

  defines function

    safeFileRead()
      stdout <- Stdout()

      try
        -> fileConn <- FileConn("log.txt")
        content <- fileConn.readAll()
        stdout.println(content)

Common mistakes

E81056 — A closeable resource was created but never closed via try-with-resources and does not escape the scope. This is a guaranteed resource leak. Wrap it in try-with-resources. See ek9 -h E81056 for details.

Incorrect:

      fileConn <- FileConn("log.txt")
      content <- fileConn.readAll()
      stdout.println(content)

Correct:

      try
        -> fileConn <- FileConn("log.txt")
        content <- fileConn.readAll()
        stdout.println(content)
Other ways to ask this
  • What triggers E81056 resource never closed or escaped?
  • How does EK9 detect abandoned resources?
  • Why must closeable resources be closed or transferred?

Coming from another language?

Java: unclosed resources only detected by FindBugs/SpotBugs, not the compiler. Python: no detection, relies on linters. Rust: Drop trait guarantees cleanup. Go: no detection (defer is voluntary). C#: Roslyn CA2000 warns but code still compiles. EK9: compile-time error E81056, abandoned resources cannot exist.

Keywords: close, leak, try, transfer, E81056, escape, ownership, abandoned, resource