AsyncPostgresSaver - unsupported Unicode escape sequence #248

Closed
opened 2026-02-20 17:33:36 -05:00 by yindo · 1 comment
Owner

Originally created by @istvancsabakis on GitHub (Sep 27, 2024).

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangGraph/LangChain documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangGraph/LangChain rather than my code.
  • I am sure this is better as an issue rather than a GitHub discussion, since this is a LangGraph bug and not a design question.

Example Code

from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from langgraph.prebuilt import create_react_agent

from typing import Literal

from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

@tool
def get_weather(city: Literal["nyc", "sf"]):
    """Use this to get weather information."""
    if city == "nyc":
        return "It might be cloudy in nyc"
    elif city == "sf":
        return "It's always sunny in sf"
    else:
        raise AssertionError("Unknown city")

tools = [get_weather]
model = ChatOpenAI(model_name="gpt-4o-mini")

async with AsyncPostgresSaver.from_conn_string(DB_URI) as checkpointer:
    graph = create_react_agent(model, tools=tools, checkpointer=checkpointer)
    config = {"configurable": {"thread_id": "6"}}
    res = await graph.ainvoke(
        {"messages": [("human", "what's the \x00 weather in nyc")]}, config
    )
    checkpoint_tuples = [c async for c in checkpointer.alist(config)]

Error Message and Stack Trace (if applicable)

UntranslatableCharacter                   Traceback (most recent call last)
Cell In[15], line 4
      2 graph = create_react_agent(model, tools=tools, checkpointer=checkpointer)
      3 config = {"configurable": {"thread_id": "6"}}
----> 4 res = await graph.ainvoke(
      5     {"messages": [("human", "what's the \x00 weather in nyc")]}, config
      6 )
      7 checkpoint_tuples = [c async for c in checkpointer.alist(config)]

File /venv/lib/python3.12/site-packages/langgraph/pregel/__init__.py:1592, in Pregel.ainvoke(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, **kwargs)
   1590 else:
   1591     chunks = []
-> 1592 async for chunk in self.astream(
   1593     input,
   1594     config,
   1595     stream_mode=stream_mode,
   1596     output_keys=output_keys,
   1597     interrupt_before=interrupt_before,
   1598     interrupt_after=interrupt_after,
   1599     debug=debug,
   1600     **kwargs,
   1601 ):
   1602     if stream_mode == "values":
   1603         latest = chunk

File /venv/lib/python3.12/site-packages/langgraph/pregel/__init__.py:1443, in Pregel.astream(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)
   1439 if "custom" in stream_modes:
   1440     config[CONF][CONFIG_KEY_STREAM_WRITER] = lambda c: stream.put_nowait(
   1441         ((), "custom", c)
   1442     )
-> 1443 async with AsyncPregelLoop(
   1444     input,
   1445     stream=StreamProtocol(stream.put_nowait, stream_modes),
   1446     config=config,
   1447     store=self.store,
   1448     checkpointer=checkpointer,
   1449     nodes=self.nodes,
   1450     specs=self.channels,
   1451     output_keys=output_keys,
   1452     stream_keys=self.stream_channels_asis,
   1453 ) as loop:
   1454     # create runner
   1455     runner = PregelRunner(
   1456         submit=loop.submit,
   1457         put_writes=loop.put_writes,
   1458         use_astream=do_stream is not None,
   1459     )
   1460     # enable subgraph streaming

File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:883, in AsyncPregelLoop.__aexit__(self, exc_type, exc_value, traceback)
    876 async def __aexit__(
    877     self,
    878     exc_type: Optional[Type[BaseException]],
   (...)
    881 ) -> Optional[bool]:
    882     # unwind stack
--> 883     return await asyncio.shield(
    884         self.stack.__aexit__(exc_type, exc_value, traceback)
    885     )

File /usr/local/lib/python3.12/contextlib.py:754, in AsyncExitStack.__aexit__(self, *exc_details)
    750 try:
    751     # bare "raise exc_details[1]" replaces our carefully
    752     # set-up context
    753     fixed_ctx = exc_details[1].__context__
--> 754     raise exc_details[1]
    755 except BaseException:
    756     exc_details[1].__context__ = fixed_ctx

File /usr/local/lib/python3.12/contextlib.py:737, in AsyncExitStack.__aexit__(self, *exc_details)
    735     cb_suppress = cb(*exc_details)
    736 else:
--> 737     cb_suppress = await cb(*exc_details)
    739 if cb_suppress:
    740     suppressed_exc = True

File /venv/lib/python3.12/site-packages/langgraph/pregel/executor.py:179, in AsyncBackgroundExecutor.__aexit__(self, exc_type, exc_value, traceback)
    177 try:
    178     if exc := task.exception():
--> 179         raise exc
    180 except asyncio.CancelledError:
    181     pass

File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:806, in AsyncPregelLoop._checkpointer_put_after_previous(self, prev, config, checkpoint, metadata, new_versions)
    804 try:
    805     if prev is not None:
--> 806         await prev
    807 finally:
    808     await cast(BaseCheckpointSaver, self.checkpointer).aput(
    809         config, checkpoint, metadata, new_versions
    810     )

File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:806, in AsyncPregelLoop._checkpointer_put_after_previous(self, prev, config, checkpoint, metadata, new_versions)
    804 try:
    805     if prev is not None:
--> 806         await prev
    807 finally:
    808     await cast(BaseCheckpointSaver, self.checkpointer).aput(
    809         config, checkpoint, metadata, new_versions
    810     )

    [... skipping similar frames: AsyncPregelLoop._checkpointer_put_after_previous at line 806 (1 times)]

File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:806, in AsyncPregelLoop._checkpointer_put_after_previous(self, prev, config, checkpoint, metadata, new_versions)
    804 try:
    805     if prev is not None:
--> 806         await prev
    807 finally:
    808     await cast(BaseCheckpointSaver, self.checkpointer).aput(
    809         config, checkpoint, metadata, new_versions
    810     )

File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:808, in AsyncPregelLoop._checkpointer_put_after_previous(self, prev, config, checkpoint, metadata, new_versions)
    806         await prev
    807 finally:
--> 808     await cast(BaseCheckpointSaver, self.checkpointer).aput(
    809         config, checkpoint, metadata, new_versions
    810     )

File /venv/lib/python3.12/site-packages/langgraph/checkpoint/postgres/aio.py:267, in AsyncPostgresSaver.aput(self, config, checkpoint, metadata, new_versions)
    258 copy = checkpoint.copy()
    259 next_config = {
    260     "configurable": {
    261         "thread_id": thread_id,
   (...)
    264     }
    265 }
--> 267 async with self._cursor(pipeline=True) as cur:
    268     await cur.executemany(
    269         self.UPSERT_CHECKPOINT_BLOBS_SQL,
    270         await asyncio.to_thread(
   (...)
    276         ),
    277     )
    278     await cur.execute(
    279         self.UPSERT_CHECKPOINTS_SQL,
    280         (
   (...)
    287         ),
    288     )

File /usr/local/lib/python3.12/contextlib.py:217, in _AsyncGeneratorContextManager.__aexit__(self, typ, value, traceback)
    215 if typ is None:
    216     try:
--> 217         await anext(self.gen)
    218     except StopAsyncIteration:
    219         return False

File /venv/lib/python3.12/site-packages/langgraph/checkpoint/postgres/aio.py:340, in AsyncPostgresSaver._cursor(self, pipeline)
    336             await self.pipe.sync()
    337 elif pipeline:
    338     # a connection not in pipeline mode can only be used by one
    339     # thread/coroutine at a time, so we acquire a lock
--> 340     async with self.lock, conn.pipeline(), conn.cursor(
    341         binary=True, row_factory=dict_row
    342     ) as cur:
    343         yield cur
    344 else:

File /usr/local/lib/python3.12/contextlib.py:217, in _AsyncGeneratorContextManager.__aexit__(self, typ, value, traceback)
    215 if typ is None:
    216     try:
--> 217         await anext(self.gen)
    218     except StopAsyncIteration:
    219         return False

File /venv/lib/python3.12/site-packages/psycopg/connection_async.py:398, in AsyncConnection.pipeline(self)
    395         pipeline = self._pipeline = AsyncPipeline(self)
    397 try:
--> 398     async with pipeline:
    399         yield pipeline
    400 finally:

File /venv/lib/python3.12/site-packages/psycopg/_pipeline.py:266, in AsyncPipeline.__aexit__(self, exc_type, exc_val, exc_tb)
    264         logger.warning("error ignored terminating %r: %s", self, exc2)
    265     else:
--> 266         raise exc2.with_traceback(None)
    267 finally:
    268     self._exit(exc_val)

Description

UntranslatableCharacter Exception When Processing Input with Null Character (\x00) or ('x0000)

Description

I encountered an UntranslatableCharacter exception when attempting to process input containing a null character (\x00) using the langgraph library. The exception occurs when invoking the graph with a message that includes the null character.

To Reproduce

  1. Set up the environment:

    • Python version: 3.12
    • Ensure langgraph and its dependencies are installed (including psycopg for PostgreSQL interaction).
  2. Use the following code snippet:

    graph = create_react_agent(model, tools=tools, checkpointer=checkpointer)
    config = {"configurable": {"thread_id": "6"}}
    res = await graph.ainvoke(
        {"messages": [("human", "what's the \x00 weather in nyc")]}, config
    )
    checkpoint_tuples = [c async for c in checkpointer.alist(config)]
    
  3. Run the code and observe the exception.

Expected Behavior

The system should handle inputs containing null characters gracefully, either by processing the input correctly or by providing a clear and informative error message indicating that null characters are not supported.

Actual Behavior

An UntranslatableCharacter exception is raised, and the process fails with the following stack trace:

System Info

langgraph==0.2.26
langgraph-checkpoint==1.0.11
langgraph-checkpoint-postgres==1.0.8

langchain==0.3.0
langchain-community==0.3.0
langchain-core==0.3.5
langchain-elasticsearch==0.3.0
langchain-google-community==2.0.0
langchain-openai==0.2.0
langchain-text-splitters==0.3.0
langchain-unstructured==0.1.4
langchainhub==0.1.21

Originally created by @istvancsabakis on GitHub (Sep 27, 2024). ### Checked other resources - [X] I added a very descriptive title to this issue. - [X] I searched the [LangGraph](https://langchain-ai.github.io/langgraph/)/LangChain documentation with the integrated search. - [X] I used the GitHub search to find a similar question and didn't find it. - [X] I am sure that this is a bug in LangGraph/LangChain rather than my code. - [X] I am sure this is better as an issue [rather than a GitHub discussion](https://github.com/langchain-ai/langgraph/discussions/new/choose), since this is a LangGraph bug and not a design question. ### Example Code ```python from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver from langgraph.prebuilt import create_react_agent from typing import Literal from langchain_core.tools import tool from langchain_openai import ChatOpenAI @tool def get_weather(city: Literal["nyc", "sf"]): """Use this to get weather information.""" if city == "nyc": return "It might be cloudy in nyc" elif city == "sf": return "It's always sunny in sf" else: raise AssertionError("Unknown city") tools = [get_weather] model = ChatOpenAI(model_name="gpt-4o-mini") async with AsyncPostgresSaver.from_conn_string(DB_URI) as checkpointer: graph = create_react_agent(model, tools=tools, checkpointer=checkpointer) config = {"configurable": {"thread_id": "6"}} res = await graph.ainvoke( {"messages": [("human", "what's the \x00 weather in nyc")]}, config ) checkpoint_tuples = [c async for c in checkpointer.alist(config)] ``` ### Error Message and Stack Trace (if applicable) ```shell UntranslatableCharacter Traceback (most recent call last) Cell In[15], line 4 2 graph = create_react_agent(model, tools=tools, checkpointer=checkpointer) 3 config = {"configurable": {"thread_id": "6"}} ----> 4 res = await graph.ainvoke( 5 {"messages": [("human", "what's the \x00 weather in nyc")]}, config 6 ) 7 checkpoint_tuples = [c async for c in checkpointer.alist(config)] File /venv/lib/python3.12/site-packages/langgraph/pregel/__init__.py:1592, in Pregel.ainvoke(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, **kwargs) 1590 else: 1591 chunks = [] -> 1592 async for chunk in self.astream( 1593 input, 1594 config, 1595 stream_mode=stream_mode, 1596 output_keys=output_keys, 1597 interrupt_before=interrupt_before, 1598 interrupt_after=interrupt_after, 1599 debug=debug, 1600 **kwargs, 1601 ): 1602 if stream_mode == "values": 1603 latest = chunk File /venv/lib/python3.12/site-packages/langgraph/pregel/__init__.py:1443, in Pregel.astream(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs) 1439 if "custom" in stream_modes: 1440 config[CONF][CONFIG_KEY_STREAM_WRITER] = lambda c: stream.put_nowait( 1441 ((), "custom", c) 1442 ) -> 1443 async with AsyncPregelLoop( 1444 input, 1445 stream=StreamProtocol(stream.put_nowait, stream_modes), 1446 config=config, 1447 store=self.store, 1448 checkpointer=checkpointer, 1449 nodes=self.nodes, 1450 specs=self.channels, 1451 output_keys=output_keys, 1452 stream_keys=self.stream_channels_asis, 1453 ) as loop: 1454 # create runner 1455 runner = PregelRunner( 1456 submit=loop.submit, 1457 put_writes=loop.put_writes, 1458 use_astream=do_stream is not None, 1459 ) 1460 # enable subgraph streaming File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:883, in AsyncPregelLoop.__aexit__(self, exc_type, exc_value, traceback) 876 async def __aexit__( 877 self, 878 exc_type: Optional[Type[BaseException]], (...) 881 ) -> Optional[bool]: 882 # unwind stack --> 883 return await asyncio.shield( 884 self.stack.__aexit__(exc_type, exc_value, traceback) 885 ) File /usr/local/lib/python3.12/contextlib.py:754, in AsyncExitStack.__aexit__(self, *exc_details) 750 try: 751 # bare "raise exc_details[1]" replaces our carefully 752 # set-up context 753 fixed_ctx = exc_details[1].__context__ --> 754 raise exc_details[1] 755 except BaseException: 756 exc_details[1].__context__ = fixed_ctx File /usr/local/lib/python3.12/contextlib.py:737, in AsyncExitStack.__aexit__(self, *exc_details) 735 cb_suppress = cb(*exc_details) 736 else: --> 737 cb_suppress = await cb(*exc_details) 739 if cb_suppress: 740 suppressed_exc = True File /venv/lib/python3.12/site-packages/langgraph/pregel/executor.py:179, in AsyncBackgroundExecutor.__aexit__(self, exc_type, exc_value, traceback) 177 try: 178 if exc := task.exception(): --> 179 raise exc 180 except asyncio.CancelledError: 181 pass File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:806, in AsyncPregelLoop._checkpointer_put_after_previous(self, prev, config, checkpoint, metadata, new_versions) 804 try: 805 if prev is not None: --> 806 await prev 807 finally: 808 await cast(BaseCheckpointSaver, self.checkpointer).aput( 809 config, checkpoint, metadata, new_versions 810 ) File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:806, in AsyncPregelLoop._checkpointer_put_after_previous(self, prev, config, checkpoint, metadata, new_versions) 804 try: 805 if prev is not None: --> 806 await prev 807 finally: 808 await cast(BaseCheckpointSaver, self.checkpointer).aput( 809 config, checkpoint, metadata, new_versions 810 ) [... skipping similar frames: AsyncPregelLoop._checkpointer_put_after_previous at line 806 (1 times)] File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:806, in AsyncPregelLoop._checkpointer_put_after_previous(self, prev, config, checkpoint, metadata, new_versions) 804 try: 805 if prev is not None: --> 806 await prev 807 finally: 808 await cast(BaseCheckpointSaver, self.checkpointer).aput( 809 config, checkpoint, metadata, new_versions 810 ) File /venv/lib/python3.12/site-packages/langgraph/pregel/loop.py:808, in AsyncPregelLoop._checkpointer_put_after_previous(self, prev, config, checkpoint, metadata, new_versions) 806 await prev 807 finally: --> 808 await cast(BaseCheckpointSaver, self.checkpointer).aput( 809 config, checkpoint, metadata, new_versions 810 ) File /venv/lib/python3.12/site-packages/langgraph/checkpoint/postgres/aio.py:267, in AsyncPostgresSaver.aput(self, config, checkpoint, metadata, new_versions) 258 copy = checkpoint.copy() 259 next_config = { 260 "configurable": { 261 "thread_id": thread_id, (...) 264 } 265 } --> 267 async with self._cursor(pipeline=True) as cur: 268 await cur.executemany( 269 self.UPSERT_CHECKPOINT_BLOBS_SQL, 270 await asyncio.to_thread( (...) 276 ), 277 ) 278 await cur.execute( 279 self.UPSERT_CHECKPOINTS_SQL, 280 ( (...) 287 ), 288 ) File /usr/local/lib/python3.12/contextlib.py:217, in _AsyncGeneratorContextManager.__aexit__(self, typ, value, traceback) 215 if typ is None: 216 try: --> 217 await anext(self.gen) 218 except StopAsyncIteration: 219 return False File /venv/lib/python3.12/site-packages/langgraph/checkpoint/postgres/aio.py:340, in AsyncPostgresSaver._cursor(self, pipeline) 336 await self.pipe.sync() 337 elif pipeline: 338 # a connection not in pipeline mode can only be used by one 339 # thread/coroutine at a time, so we acquire a lock --> 340 async with self.lock, conn.pipeline(), conn.cursor( 341 binary=True, row_factory=dict_row 342 ) as cur: 343 yield cur 344 else: File /usr/local/lib/python3.12/contextlib.py:217, in _AsyncGeneratorContextManager.__aexit__(self, typ, value, traceback) 215 if typ is None: 216 try: --> 217 await anext(self.gen) 218 except StopAsyncIteration: 219 return False File /venv/lib/python3.12/site-packages/psycopg/connection_async.py:398, in AsyncConnection.pipeline(self) 395 pipeline = self._pipeline = AsyncPipeline(self) 397 try: --> 398 async with pipeline: 399 yield pipeline 400 finally: File /venv/lib/python3.12/site-packages/psycopg/_pipeline.py:266, in AsyncPipeline.__aexit__(self, exc_type, exc_val, exc_tb) 264 logger.warning("error ignored terminating %r: %s", self, exc2) 265 else: --> 266 raise exc2.with_traceback(None) 267 finally: 268 self._exit(exc_val) ``` ### Description # UntranslatableCharacter Exception When Processing Input with Null Character (`\x00`) or (`'x0000`) ## Description I encountered an `UntranslatableCharacter` exception when attempting to process input containing a null character (`\x00`) using the `langgraph` library. The exception occurs when invoking the graph with a message that includes the null character. ## To Reproduce 1. **Set up the environment:** - Python version: 3.12 - Ensure `langgraph` and its dependencies are installed (including `psycopg` for PostgreSQL interaction). 2. **Use the following code snippet:** ```python graph = create_react_agent(model, tools=tools, checkpointer=checkpointer) config = {"configurable": {"thread_id": "6"}} res = await graph.ainvoke( {"messages": [("human", "what's the \x00 weather in nyc")]}, config ) checkpoint_tuples = [c async for c in checkpointer.alist(config)] ``` 3. **Run the code and observe the exception.** ## Expected Behavior The system should handle inputs containing null characters gracefully, either by processing the input correctly or by providing a clear and informative error message indicating that null characters are not supported. ## Actual Behavior An `UntranslatableCharacter` exception is raised, and the process fails with the following stack trace: ### System Info langgraph==0.2.26 langgraph-checkpoint==1.0.11 langgraph-checkpoint-postgres==1.0.8 langchain==0.3.0 langchain-community==0.3.0 langchain-core==0.3.5 langchain-elasticsearch==0.3.0 langchain-google-community==2.0.0 langchain-openai==0.2.0 langchain-text-splitters==0.3.0 langchain-unstructured==0.1.4 langchainhub==0.1.21
yindo closed this issue 2026-02-20 17:33:36 -05:00
Author
Owner

@vbarda commented on GitHub (Sep 27, 2024):

@istvancsabakis thanks for reporting -- this is fixed in langgraph-checkpoint-postgres==1.0.9. please let me know if you run into any other issues!

@vbarda commented on GitHub (Sep 27, 2024): @istvancsabakis thanks for reporting -- this is fixed in `langgraph-checkpoint-postgres==1.0.9`. please let me know if you run into any other issues!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#248