{"record":{"id":"3361636f6a4eb09a","repo":"apache/beam","slug":"cannot-pickle-files-that-map-to-tty-objects","errorCode":null,"errorMessage":"Cannot pickle files that map to tty objects","messagePattern":"Cannot pickle files that map to tty objects","errorType":"exception","errorClass":"PicklingError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/internal/cloudpickle/cloudpickle.py","lineNumber":1082,"sourceCode":"\n\ndef _file_reduce(obj):\n  \"\"\"Save a file.\"\"\"\n  import io\n\n  if not hasattr(obj, \"name\") or not hasattr(obj, \"mode\"):\n    raise pickle.PicklingError(\n        \"Cannot pickle files that do not map to an actual file\")\n  if obj is sys.stdout:\n    return getattr, (sys, \"stdout\")\n  if obj is sys.stderr:\n    return getattr, (sys, \"stderr\")\n  if obj is sys.stdin:\n    raise pickle.PicklingError(\"Cannot pickle standard input\")\n  if obj.closed:\n    raise pickle.PicklingError(\"Cannot pickle closed files\")\n  if hasattr(obj, \"isatty\") and obj.isatty():\n    raise pickle.PicklingError(\"Cannot pickle files that map to tty objects\")\n  if \"r\" not in obj.mode and \"+\" not in obj.mode:\n    raise pickle.PicklingError(\n        \"Cannot pickle files that are not opened for reading: %s\" % obj.mode)\n\n  name = obj.name\n\n  retval = io.StringIO()\n\n  try:\n    # Read the whole file\n    curloc = obj.tell()\n    obj.seek(0)\n    contents = obj.read()\n    obj.seek(curloc)\n  except OSError as e:\n    raise pickle.PicklingError(\n        \"Cannot pickle file %s as it cannot be read\" % name) from e\n  retval.write(contents)","sourceCodeStart":1064,"sourceCodeEnd":1100,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/internal/cloudpickle/cloudpickle.py#L1064-L1100","documentation":"Beam's cloudpickle will not serialize file objects attached to a terminal (isatty() true), such as stdin/stdout variants or pseudo-terminals, because a tty cannot be reconstructed on a remote worker. Pickling raises pickle.PicklingError('Cannot pickle files that map to tty objects').","triggerScenarios":"Cloudpickling a closure capturing a tty file object, e.g. sys.stdout when running under a terminal (if not caught by the earlier identity checks), os.openpty() handles, or pty-backed streams.","commonSituations":"Running Beam jobs from an interactive shell where code references stdout/stderr-like tty streams; debug logging handles bound to the terminal captured in DoFns; test harnesses using pty objects.","solutions":["Replace the tty reference with a real file: open a log file and capture to it instead.","Read terminal output in the main process; pass data, not the handle, to workers.","Use logging module rather than holding a tty stream in serialized state.","Strip the attribute before pickling (e.g. in __getstate__)."],"exampleFix":"// before\nclass Job: out = sys.stdout  # tty when run interactively\n// after\nclass Job: out = open('job.log', 'w')","handlingStrategy":"validation","validationCode":"if hasattr(obj, 'isatty') and obj.isatty():\n    raise ValueError('tty file object cannot be pickled')","typeGuard":"def is_non_tty_file(obj):\n    return not (hasattr(obj, 'isatty') and obj.isatty())","tryCatchPattern":"try:\n    cloudpickle.dump(fn)\nexcept pickle.PicklingError as e:\n    if 'tty' in str(e): fn = replace_tty_with_file(fn)\n    else: raise","preventionTips":["Redirect output to files or logging instead of tty streams","Do not capture console streams in serialized code","Test pipeline submission from non-interactive contexts"],"tags":["python","pickle","serialization","tty"],"backgroundTag":"json-serialization-failed","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}