Repository: nats-io/nats.py (language: python)

Issue:
Passing wrong stream name to `add_stream` throws `errors.TimeoutError`
This may be similar to #305 and/or #437.

Following code:
```python
import nats
import pytest

@pytest.mark.asyncio
async def test_js_wrong_name():
    nc = await nats.connect("localhost")
    js = nc.jetstream()
    await js.add_stream(name="stream.with.wrong.name")

    await nc.close()
```

raises timeout on `js.add_stream`:

```
>           raise errors.TimeoutError
E           nats.errors.TimeoutError: nats: timeout

../.venv/lib/python3.11/site-packages/nats/aio/client.py:1025: TimeoutError
```

If the stream name is correct it's ok.


Discussion / hints:
Bump. This issue caused unnecessary confusion. 
Just having a "." in the name causes a timeout. Should return a helpful bad request error instead.

Fix patch:
diff --git a/nats/js/manager.py b/nats/js/manager.py
index df7dd863..98648655 100644
--- a/nats/js/manager.py
+++ b/nats/js/manager.py
@@ -91,12 +91,26 @@ async def add_stream(
         if config is None:
             config = api.StreamConfig()
         config = config.evolve(**params)
-        if config.name is None:
+
+        stream_name = config.name
+        if stream_name is None:
             raise ValueError("nats: stream name is required")
 
+        # Validate stream name
+        invalid_chars = set(".*>/\\")
+        has_invalid_chars = any(char in stream_name for char in invalid_chars)
+        has_whitespace = any(char.isspace() for char in stream_name)
+        is_not_printable = not stream_name.isprintable()
+
+        if has_invalid_chars or has_whitespace or is_not_printable:
+            raise ValueError(
+                f"nats: stream name ({stream_name}) is invalid. Names cannot contain whitespace, '.', '*', '>', "
+                "path separators (forward or backward slash), or non-printable characters."
+            )
+
         data = json.dumps(config.as_dict())
         resp = await self._api_request(
-            f"{self._prefix}.STREAM.CREATE.{config.name}",
+            f"{self._prefix}.STREAM.CREATE.{stream_name}",
             data.encode(),
             timeout=self._timeout,
         )


Test patch:
diff --git a/tests/test_js.py b/tests/test_js.py
index ec61391b..f25910c7 100644
--- a/tests/test_js.py
+++ b/tests/test_js.py
@@ -12,6 +12,7 @@
 import io
 import tempfile
 from unittest.mock import AsyncMock
+import re
 
 import pytest
 import nats
@@ -4267,3 +4268,33 @@ async def test_fetch_pull_subscribe_bind(self):
         assert str(err.value) == 'nats: stream name is required'
 
         await nc.close()
+
+
+class BadStreamNamesTest(SingleJetStreamServerTestCase):
+    @async_test
+    async def test_add_stream_invalid_names(self):
+        nc = NATS()
+        await nc.connect()
+        js = nc.jetstream()
+
+        invalid_names = [
+            "stream name with spaces",
+            "stream.name.with.dots",
+            "stream*name*with*asterisks",
+            "stream>name>with>greaterthans",
+            "stream/name/with/forwardslashes",
+            "stream\\name\\with\\backslashes",
+            "stream\nname\nwith\nnewlines",
+            "stream\tname\twith\ttabs",
+            "stream\x00name\x00with\x00nulls",
+        ]
+
+        for name in invalid_names:
+            with pytest.raises(
+                ValueError,
+                match=(
+                    f"nats: stream name \\({re.escape(name)}\\) is invalid. Names cannot contain whitespace, '\\.', "
+                    "'\\*', '>', path separators \\(forward or backward slash\\), or non-printable characters."
+                ),
+            ):
+                await js.add_stream(name=name)