Repository navigation
startup crashes on TypeError: replace() argument 2 must be str, not bytes, followed by TypeError: cannot use a string pattern on a bytes-like object #1518
Description
Installed pgcli 4.3.0 using pip install pgcli in a Python 3.13 venv (MacOS 15.x, recent Sequoia).
pgcli crashes on startup due to a string-handling error (or byte string-handling error). Superficial patches lead to deeper problems during startup.
First I tried to start pgcli with the command pgcli my_database_name
Got a stacktrace ending with this error, related to handling string versus bytes:
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/main.py", line 1051, in get_message
prompt = self.get_prompt(prompt_format)
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/main.py", line 1323, in get_prompt
string = string.replace("\\H", self.pgexecute.host or "(none)")
TypeError: replace() argument 2 must be str, not bytes
I modified line 1323 like this, and this initial error went away...
string = string.replace("\\H", str(self.pgexecute.host) or "(none)") # HACK added str() 20250802
...however then I got the same error from the subsequent line 1324:
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/main.py", line 1324, in get_prompt
string = string.replace("\\h", self.pgexecute.short_host or "(none)")
TypeError: replace() argument 2 must be str, not bytes
So I modified line 1324 the same way to bypass the error:
string = string.replace("\\h", str(self.pgexecute.short_host) or "(none)") # HACK added str() 20250802
These two hacks permitted pgcli to get farther in start up, however then I received another TypeError, seemingly related to the same issues. At this point I gave up debugging it. It should be an easy fix for someone who knows the pgcli codebase.
$ pgcli my_database_name
Using local time zone MyCountry/MyCity (server uses b'MyCountry/MyCity')
Use `set time zone <TZ>` to override, or set `use_local_timezone = False` in the config
Exception in thread completion_refresh:
Traceback (most recent call last):
File "/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/threading.py", line 1043, in _bootstrap_inner
self.run()
~~~~~~~~^^
File "/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/threading.py", line 994, in run
self._target(*self._args, **self._kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/completion_refresher.py", line 67, in _bg_refresh
refresher(completer, executor)
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
Server: PostgreSQL 17.5 (Homebrew)
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/completion_refresher.py", line 108, in refresh_schemata
completer.set_search_path(executor.search_path())
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/pgcompleter.py", line 335, in set_search_path
self.search_path = self.escaped_names(search_path)
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
Version: 4.3.0
Home: http://pgcli.com
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/pgcompleter.py", line 183, in escaped_names
return [self.escape_name(name) for name in names]
~~~~~~~~~~~~~~~~^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/pgcompleter.py", line 164, in escape_name
(not self.name_pattern.match(name))
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
TypeError: cannot use a string pattern on a bytes-like object
Traceback (most recent call last):
File "/path/to/my/venv/bin/pgcli", line 8, in <module>
sys.exit(cli())
~~~^^
File "/path/to/my/venv/lib/python3.13/site-packages/click/core.py", line 1442, in __call__
return self.main(*args, **kwargs)
~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/click/core.py", line 1363, in main
rv = self.invoke(ctx)
File "/path/to/my/venv/lib/python3.13/site-packages/click/core.py", line 1226, in invoke
return ctx.invoke(self.callback, **ctx.params)
~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/click/core.py", line 794, in invoke
return callback(*args, **kwargs)
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/main.py", line 1720, in cli
pgcli.run_cli()
~~~~~~~~~~~~~^^
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/main.py", line 979, in run_cli
text = self.prompt_app.prompt()
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/shortcuts/prompt.py", line 1035, in prompt
return self.app.run(
~~~~~~~~~~~~^
set_exception_handler=set_exception_handler,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<2 lines>...
inputhook=inputhook,
^^^^^^^^^^^^^^^^^^^^
)
^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/application/application.py", line 1002, in run
return asyncio.run(coro)
~~~~~~~~~~~^^^^^^
File "/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 195, in run
return runner.run(main)
~~~~~~~~~~^^^^^^
File "/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py", line 725, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/application/application.py", line 886, in run_async
return await _run_async(f)
^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/application/application.py", line 739, in _run_async
self._redraw()
~~~~~~~~~~~~^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/application/application.py", line 543, in _redraw
self.context.copy().run(run_in_context)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/application/application.py", line 526, in run_in_context
self.renderer.render(self, self.layout)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/renderer.py", line 647, in render
layout.container.preferred_height(size.columns, size.rows).preferred,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/layout/containers.py", line 319, in preferred_height
c.preferred_height(width, max_available_height) for c in self._all_children
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/layout/containers.py", line 786, in preferred_height
return self.content.preferred_height(width, max_available_height)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/layout/containers.py", line 319, in preferred_height
c.preferred_height(width, max_available_height) for c in self._all_children
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/layout/containers.py", line 2623, in preferred_height
if self.filter():
~~~~~~~~~~~^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/filters/base.py", line 253, in __call__
return self.func()
~~~~~~~~~^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/shortcuts/prompt.py", line 161, in has_before_fragments
for fragment, char, *_ in get_prompt_text():
~~~~~~~~~~~~~~~^^
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/shortcuts/prompt.py", line 1280, in _get_prompt
return to_formatted_text(self.message, style="class:prompt")
File "/path/to/my/venv/lib/python3.13/site-packages/prompt_toolkit/formatted_text/base.py", line 82, in to_formatted_text
return to_formatted_text(value(), style=style)
~~~~~^^
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/main.py", line 1051, in get_message
prompt = self.get_prompt(prompt_format)
File "/path/to/my/venv/lib/python3.13/site-packages/pgcli/main.py", line 1324, in get_prompt
string = string.replace("\\h", self.pgexecute.short_host or "(none)")
TypeError: replace() argument 2 must be str, not bytes
Activity
dbaty commentedon Aug 2, 2025
I cannot reproduce the error. It's certainly linked to the name of the database host. Could you add some debugging statements before the problematic line, like this?
As you have noticed, the host name seems to be bytes while we expect it to be a string. I am not sure where that comes from. If you're willing to help, you could also try to add debugging statements in
PGExecute.connect()(inpgexecute.py) to see what sets thehostattribute. It would help us determine the cause of the bug.tekpriest commentedon Feb 19, 2026
I am experiencing same issue, installed via homebrew, I was able to get it working like this
pgcli -U root -h localhost -p 5432but it showed this errors before opening
checking the $XDG_CONFIG/pgcli/log file, I got this
so I thought, could the error be linked to the missing module
keyring? So I installed that with pip usingpip3 install -U keyringand brewbrew install keyringbut same error still shows in the log fileand this in the terminal
using the alias dsn section does seem to work without any errors.
Maybe this can help anyone
DiegoDAF commentedon Sep 1, 2026
Hi,
Thanks for the report. I can reproduce this and I think the three symptoms
(
TypeErroron the prompt, theb'...'-prefixed timezone message, and thecompletion_refreshthread dying inparse_defaults) all share one rootcause.
When the client encoding cannot decode a text value (most commonly
SQL_ASCII), psycopg returns the text columns to us as rawbytesinstead ofstr. A few code paths were passing those values through unguarded:socket connection raised
TypeError: expected str, not bytes.show timezoneresult was printed in the startup message, showing ab'...'-prefixed value.selected text columns such as
arg_defaults; parsing those bytes withparse_defaults()crashed the refresh thread withTypeError: can only concatenate str (not "int") to str.The fix is to decode those values defensively when they come back as
bytes(same guard we already use in the completion metadata fix for #1405). It is a
small, self-contained change: a
_decode_if_bytes()helper plus decoding thesocket directory, the timezone, and the function metadata rows. I have
reproduced it against a real
SQL_ASCIIcluster (3396 catalog functions, allmetadata fields come back as plain
strafter the fix) and added unit tests.A small PR with tests is coming. I'll link it here once it is in.
its the same for #1484
Defensively decode SQL_ASCII text columns in prompt, timezone, and
DiegoDAF commentedon Sep 1, 2026
Hi,
Thanks for the report. I can reproduce this and I think the three symptoms (
TypeErroron the prompt, theb'...'-prefixed timezone message, and thecompletion_refreshthread dying inparse_defaults) all share one root cause.When the client encoding cannot decode a text value (most commonly
SQL_ASCII), psycopg returns the text columns to us as rawbytesinstead ofstr. A few code paths were passing those values through unguarded:TypeError: expected str, not bytes.show timezoneresult was printed in the startup message, showing ab'...'-prefixed value.arg_defaults; parsing those bytes withparse_defaults()crashed the refresh thread withTypeError: can only concatenate str (not "int") to str.The fix is to decode those values defensively when they come back as
bytes(same guard we already use in the completion metadata fix for #1405). It is a small, self-contained change: a_decode_if_bytes()helper plus decoding the socket directory, the timezone, and the function metadata rows. I have reproduced it against a realSQL_ASCIIcluster (3396 catalog functions, all metadata fields come back as plainstrafter the fix) and added unit tests.The fix is up as #1628.