Closing StringIO makes getvalue unavailable. Capture the string before leaving the owning context, or define explicitly which component owns and closes a caller-supplied stream.
Check the failure with a small fixture
A writer or context manager closes an in-memory stream before the caller retrieves its text.
from io import StringIO
with StringIO() as stream:
stream.write("header\n")
stream.write("row\n")
text = stream.getvalue()
assert stream.closed
assert text == "header\nrow\n"
try:
stream.getvalue()
except ValueError:
print("closed stream cannot be read")
def render_text():
buffer = StringIO()
try:
buffer.write("ready")
return buffer.getvalue()
finally:
buffer.close()
assert render_text() == "ready"What the result means
The returned string remains usable because it is the result value captured before closure. It is not the same object as the stream. Returning a StringIO from inside its with block would instead return a stream that is already closed by the time the caller receives it.
Choose the correction for your caller
If your function creates the buffer, returning text or bytes often provides the clearest ownership boundary. If another library receives your stream and closes it, inspect that library contract before overriding close or wrapping the stream. Some wrappers take ownership of their underlying buffer; other writers merely flush it. Keep any final flush needed to complete the document before capturing the value. seek(0) is useful for rereading an open stream but cannot reopen a closed StringIO.
Limits and the next check
This example is text-only. BytesIO and compression or text wrappers have their own finish and ownership requirements. A real file can be reopened by pathname, but a closed in-memory StringIO has no persistent file to reopen.
Related: Compare a related data or execution contract.
The embedded Python example was executed as an isolated local script on November 10, 2026 (KST), using Python 3.11.6. It demonstrates the stated boundary; it is not a test of your application or a promise of compatibility with every Python release.