snowflakedb / snowflakedb/snowflake-connector-python
SNOW-3794266: OAuth token caching on Windows crashes with unhandled CredWrite error 1783 when the token exceeds the Credential Manager blob limit
@sfc-gh-snow-drivers-warsaw-dl is already working on this.
Since Jul 16, 2026.
- Dominant language
- Python
- Stars
- 730
- Forks
- 574
- Avg merge
- 5h 45m
- Merged PRs (30d)
- 16
Description
Python version
Python 3.13.3 (Windows Store / MSIX distribution, but reproducible on regular CPython too)
Operating system and processor architecture
Windows 11 x64
Installed packages
acryl-datahub==1.5.0.1
agate==1.9.1
aiofile==3.9.0
aiohappyeyeballs==2.6.1
aiohttp==3.13.5
aiosignal==1.4.0
alembic==1.18.4
altair==6.0.0
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.13.0
argon2-cffi==25.1.0
argon2-cffi-bindings==25.1.0
asgiref==3.11.1
asn1crypto==1.5.1
asttokens==2.4.1
attrs==26.1.0
Authlib==1.6.9
av==17.0.0
avro==1.12.1
avro-gen3==0.7.16
babel==2.18.0
backoff==2.2.1
bandit==1.9.4
bcrypt==5.0.0
beartype==0.22.9
beautifulsoup4==4.14.3
black==26.3.1
blinker==1.9.0
boto3==1.42.76
botocore==1.42.76
cached-property==2.0.1
cachetools==7.0.5
caio==0.9.25
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.6
click==8.4.1
click-default-group==1.2.4
click-spinner==0.1.10
cloudpickle==3.1.1
colorama==0.4.6
coverage==7.13.5
cronsim==2.7
cryptography==46.0.0
ctranslate2==4.7.1
cyclopts==4.10.1
daff==1.4.2
dbt-adapters==1.24.2
dbt-common==1.38.0
dbt-core==1.11.11
dbt-extractor==0.6.0
dbt-metricflow==0.13.0
dbt-protos==1.0.514
dbt-semantic-interfaces==0.9.0
dbt-snowflake==1.11.5
deepdiff==8.6.2
Deprecated==1.3.1
devtools==0.12.2
diskcache==5.6.3
distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.17.0
docutils==0.22.4
email-validator==2.3.0
et_xmlfile==2.0.0
exceptiongroup==1.3.1
execnet==2.1.2
executing==2.2.1
expandvars==1.1.2
fakeredis==2.34.1
fastapi==0.135.3
faster-whisper==1.2.1
fastmcp==3.1.1
filelock==3.25.2
flatbuffers==25.12.19
frozenlist==1.8.0
fsspec==2026.2.0
git-filter-repo==2.47.0
gitdb==4.0.12
GitPython==3.1.44
googleapis-common-protos==1.75.0
graphql-core==3.2.8
graphviz==0.21
greenlet==3.3.2
h11==0.16.0
halo==0.0.31
hf-xet==1.4.2
httpcore==1.0.9
httptools==0.8.0
httpx==0.28.1
httpx-sse==0.4.3
huggingface_hub==1.7.1
humanfriendly==10.0
hypothesis==6.151.9
icecream==2.1.10
id==1.5.0
idna==3.11
ijson==3.5.0
importlib_metadata==8.7.1
iniconfig==2.3.0
invoke==3.0.0
isodate==0.7.2
isort==8.0.1
itsdangerous==2.2.0
jaraco.classes==3.4.0
jaraco.context==6.1.2
jaraco.functools==4.4.0
Jinja2==3.1.6
jiter==0.13.0
jmespath==1.1.0
jq==1.11.0
json-logic==0.7.0a0
jsonref==1.1.0
jsonschema==4.26.0
jsonschema-path==0.4.5
jsonschema-specifications==2025.9.1
keyring==25.7.0
keyrings.alt==5.0.2
keyrings.cryptfile==1.3.9
langfuse==4.6.1
leather==0.4.1
line_profiler==5.0.2
llvmlite==0.46.0
log-symbols==0.0.14
lupa==2.6
lxml==6.0.2
Mako==1.3.10
mando==0.7.1
markdown-it-py==4.0.0
MarkupSafe==3.0.3
mashumaro==3.14
mcp==1.26.0
mdurl==0.1.2
metricflow==0.211.0
mixpanel==5.1.0
more-itertools==10.8.0
mpmath==1.3.0
msal==1.35.1
msgpack==1.1.2
multidict==6.7.1
mypy_extensions==1.1.0
narwhals==2.18.0
networkx==3.6.1
nodeenv==1.10.0
numba==0.64.0
numpy==2.4.3
nvidia-ml-py==13.595.45
onnxruntime==1.24.4
openai==2.30.0
openapi-pydantic==0.5.1
openlineage-python==1.50.0
openpyxl==3.1.5
opentelemetry-api==1.41.1
opentelemetry-exporter-otlp-proto-common==1.41.1
opentelemetry-exporter-otlp-proto-http==1.41.1
opentelemetry-proto==1.41.1
opentelemetry-sdk==1.41.1
opentelemetry-semantic-conventions==0.62b1
orderly-set==5.5.0
orjson==3.11.7
packaging==25.0
pandas==2.3.3
paramiko==4.0.0
parsedatetime==2.6
pathable==0.5.0
pathspec==1.1.1
pathvalidate==3.3.1
pillow==12.1.1
platformdirs==4.9.4
playwright==1.60.0
plotly==5.24.1
pluggy==1.6.0
progressbar2==4.5.0
prometheus_client==0.24.1
prompt_toolkit==3.0.51
propcache==0.4.1
protobuf==6.33.6
psutil==5.9.8
psycopg2-binary==2.9.11
PuLP==3.3.2
py-cpuinfo==9.0.0
py-key-value-aio==0.4.4
py-key-value-shared==0.3.0
pyarrow==23.0.1
pycparser==3.0
pycryptodome==3.23.0
pydantic==2.12.5
pydantic-settings==2.13.1
pydantic_core==2.41.5
pydeck==0.9.1
pydocket==0.18.2
pyee==13.0.1
Pygments==2.19.2
pyinstrument==5.1.2
PyJWT==2.12.1
PyMuPDF==1.27.2.2
PyNaCl==1.6.2
pyodbc==5.3.0
pyOpenSSL==25.3.0
pypdf==6.12.2
pyperclip==1.11.0
pyreadline3==3.5.4
pyright==1.1.408
pytest==9.0.2
pytest-anyio==0.0.0
pytest-benchmark==5.2.3
pytest-cov==7.0.0
pytest-mock==3.15.1
pytest-sugar==1.1.1
pytest-timeout==2.4.0
pytest-xdist==3.8.0
python-dateutil==2.9.0.post0
python-docx==1.2.0
python-dotenv==1.2.1
python-json-logger==4.0.0
python-multipart==0.0.22
python-pptx==1.0.2
python-slugify==8.0.4
python-utils==3.9.1
pytimeparse==1.1.8
pytokens==0.4.1
pytz==2025.2
pywin32==311
pywin32-ctypes==0.2.3
PyYAML==6.0.2
radon==6.0.1
RapidFuzz==3.14.5
redis==7.4.0
referencing==0.37.0
regex==2026.2.28
requests==2.32.4
requests-file==3.0.1
requirements-parser==0.13.0
rich==14.0.0
rich-rst==1.3.2
rpds-py==0.30.0
ruamel.yaml==0.18.17
ruamel.yaml.clib==0.2.15
ruff==0.15.7
s3transfer==0.16.0
scalene==2.2.1
scipy==1.17.1
sentry-sdk==2.57.0
setuptools==80.8.0
shellingham==1.5.4
six==1.17.0
smmap==5.0.3
sniffio==1.3.1
snowflake-cli==3.16.0
snowflake-connector-python==3.18.0
snowflake-labs-mcp @ file:///C:/Users/carlos.alemany/OneDrive%20-%20civica-soft.com/Documentos/UEFA/snowflake-mcp
snowflake-snowpark-python==1.41.0
snowflake.core==1.10.0
snowplow-tracker==1.1.0
sortedcontainers==2.4.0
soupsieve==2.8.3
spinners==0.0.24
SQLAlchemy==2.0.44
sqlglot==30.0.3
sqlparse==0.5.3
sse-starlette==3.3.3
starlette==1.0.0
stevedore==5.7.0
streamlit==1.58.0
sympy==1.14.0
tabulate==0.9.0
tenacity==9.1.4
termcolor==3.3.0
text-unidecode==1.3
tiktoken==0.12.0
tokenizers==0.22.2
toml==0.10.2
tomlkit==0.13.3
tornado==6.5.5
tqdm==4.67.3
ty==0.0.25
typer==0.17.3
typing-inspect==0.9.0
typing-inspection==0.4.2
typing_extensions==4.15.0
tzdata==2025.3
tzlocal==5.3.1
uncalled-for==0.2.0
update-checker==0.18.0
urllib3==2.6.3
uvicorn==0.42.0
vulture==2.16
watchdog==6.0.0
watchfiles==1.1.1
wcwidth==0.6.0
websockets==16.0
wheel==0.47.0
wrapt==1.17.3
xlsxwriter==3.2.9
yarl==1.23.0
zipp==3.23.0
What did you do?
I set up a snow CLI connection with OAuth and token caching enabled:
[connections.oauth-conn]
account = "<account>.<region>"
user = "<user>"
authenticator = "OAUTH_AUTHORIZATION_CODE"
client_store_temporary_credential = true
(ALTER ACCOUNT SET ALLOW_ID_TOKEN = true is set on the account.)
Every invocation completes the browser auth and then aborts:
$ snow sql -c oauth-conn -q "select 1"
...
An unexpected exception occurred. Use --debug option to see the traceback. Exception message:
(1783, 'CredWrite', 'The stub received bad data')
Since nothing was cached, the next invocation opens the browser again, and crashes again. The feature is effectively unusable for OAuth on Windows.
The cause is a hard limit in the Windows Credential Manager: CRED_MAX_CREDENTIAL_BLOB_SIZE is 2560 bytes, and keyring stores the blob as UTF-16, so anything over 1280 characters fails CredWrite with error 1783. My OAuth access token is ~1580 characters (~3160 bytes). The externalbrowser ID_TOKEN is only ~350 characters, which is why externalbrowser caching works fine on Windows and this only bites OAuth (and presumably any IdP that issues large JWTs). Same underlying limit as jaraco/keyring#355, python-poetry/poetry#6597, danieljoos/wincred#18.
The reason it crashes the whole command rather than just skipping the cache is an exception-type mismatch in KeyringTokenCache.store:
except keyring.errors.KeyringError as ke:
self.logger.error("Could not store id_token to keyring, %s", str(ke))
WinVaultKeyring doesn't raise KeyringError here — the failed CredWrite surfaces as pywintypes.error (from win32ctypes.pywin32.pywintypes when pywin32-ctypes is installed, which keyring prefers), and that class derives from plain Exception:
>>> from keyring.backends.Windows import pywintypes
>>> import keyring.errors
>>> issubclass(pywintypes.error, keyring.errors.KeyringError)
False
So the except clause is dead code for this failure mode and the exception propagates out of the auth flow.
Minimal repro that needs no Snowflake account (Windows only):
import keyring
from keyring.backends.Windows import WinVaultKeyring
from snowflake.connector.token_cache import KeyringTokenCache, TokenKey, TokenType
keyring.set_keyring(WinVaultKeyring())
cache = KeyringTokenCache()
key = TokenKey("TESTUSER", "TEST.EXAMPLE.COM", TokenType.OAUTH_ACCESS_TOKEN)
cache.store(key, "x" * 1583) # raises pywintypes.error (1783, 'CredWrite', ...)
With "x" * 350 (roughly ID_TOKEN size) the same call succeeds.
What did you expect to see?
A failed cache write should never abort a connection that has already authenticated successfully. store() should catch the backend's real exception types (OSError / pywintypes.error, not just KeyringError) and degrade to a no-op with a visible warning.
2. Ideally, tokens larger than the Credential Manager limit should still be cached ; either by chunking across multiple credential entries (the approach suggested in jaraco/keyring#355) or by falling back to a file-based cache on Windows. Failing that, a clear warning ("token too large for Windows Credential Manager, caching disabled") would at least tell users why they keep getting browser prompts.
Can you set logging to DEBUG and collect the logs?
import logging
import os
for logger_name in ('snowflake.connector',):
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(logging.Formatter('%(asctime)s - %(threadName)s %(filename)s:%(lineno)d - %(funcName)s() - %(levelname)s - %(message)s'))
logger.addHandler(ch)
logs from DEBUG:
python: 3.13.14 (tags/v3.13.14:fd17997, Jun 10 2026, 13:03:48) [MSC v.1944 64 bit (AMD64)]
platform: Windows-11-10.0.26200-SP0
keyring backend: <class 'keyring.backends.Windows.WinVaultKeyring'>
issubclass(pywintypes.error, KeyringError): False
--- store of 350-char token (ID_TOKEN-sized) ---
retrieved: 350 chars
--- store of 1583-char token (OAuth-sized), expecting CredWrite 1783 ---
Traceback (most recent call last):
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\win32ctypes\pywin32\pywintypes.py", line 36, in pywin32error
yield
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\win32ctypes\pywin32\win32cred.py", line 35, in CredWrite
_authentication._CredWrite(c_pcreds, 0)
~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\win32ctypes\core\cffi\_authentication.py", line 155, in _CredWrite
return check_false(
dlls.advapi32.CredWriteW(Credential, Flags), u'CredWrite')
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\win32ctypes\core\cffi\_util.py", line 78, in __call__
self._raise_error(function_name)
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\win32ctypes\core\cffi\_util.py", line 89, in _raise_error
raise exception
OSError: [WinError 1783] El fragmento ha recibido datos incorrectos
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\dev\UEFA\datacatalogue-api-tool\scripts\collect_credwrite_debug_logs.py", line 52, in <module>
cache.store(key, "x" * 1583) # unhandled traceback expected here
~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\snowflake\connector\token_cache.py", line 372, in store
keyring.set_password(
~~~~~~~~~~~~~~~~~~~~^
key.string_key(),
^^^^^^^^^^^^^^^^^
key.user.upper(),
^^^^^^^^^^^^^^^^^
token,
^^^^^^
)
^
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\keyring\core.py", line 70, in set_password
get_keyring().set_password(service_name, username, password)
~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\keyring\backend.py", line 60, in wrapper
return orig(self, system, username, *args, **kwargs)
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\keyring\backends\Windows.py", line 134, in set_password
self._set_password(service, username, str(password))
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\keyring\backends\Windows.py", line 145, in _set_password
win32cred.CredWrite(credential, 0)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\win32ctypes\pywin32\win32cred.py", line 34, in CredWrite
with _pywin32error():
~~~~~~~~~~~~~^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.13_3.13.3824.0_x64__qbz5n2kfra8p0\Lib\contextlib.py", line 162, in __exit__
self.gen.throw(value)
~~~~~~~~~~~~~~^^^^^^^
File "C:\Users\carlos.alemany\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\win32ctypes\pywin32\pywintypes.py", line 40, in pywin32error
raise error(exception.winerror, exception.function, exception.strerror)
win32ctypes.pywin32.pywintypes.error: (1783, 'CredWrite', 'El fragmento ha recibido datos incorrectos')
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.