{"record":{"id":"433677eef0c7a729","repo":"D4Vinci/Scrapling","slug":"nested-dictionaries-are-not-accepted-only-string","errorCode":null,"errorMessage":"Nested dictionaries are not accepted, only string keys and string values are accepted","messagePattern":"Nested dictionaries are not accepted, only string keys and string values are accepted","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"scrapling/parser.py","lineNumber":731,"sourceCode":"        attributes: Dict[str, Any] = dict()\n        tags: Set[str] = set()\n        patterns: Set[Pattern] = set()\n        results, functions, selectors = Selectors(), [], []\n\n        # Brace yourself for a wonderful journey!\n        for arg in args:\n            if isinstance(arg, str):\n                tags.add(arg)\n\n            elif type(arg) in (list, tuple, set):\n                arg = cast(Iterable, arg)  # Type narrowing for type checkers like pyright\n                if not all(map(lambda x: isinstance(x, str), arg)):\n                    raise TypeError(\"Nested Iterables are not accepted, only iterables of tag names are accepted\")\n                tags.update(set(arg))\n\n            elif isinstance(arg, dict):\n                if not all([(isinstance(k, str) and isinstance(v, str)) for k, v in arg.items()]):\n                    raise TypeError(\n                        \"Nested dictionaries are not accepted, only string keys and string values are accepted\"\n                    )\n                attributes.update(arg)\n\n            elif isinstance(arg, re_Pattern):\n                patterns.add(arg)\n\n            elif callable(arg):\n                if len(signature(arg).parameters) > 0:\n                    functions.append(arg)\n                else:\n                    raise TypeError(\n                        \"Callable filter function must have at least one argument to take `Selector` objects.\"\n                    )\n\n            else:\n                raise TypeError(f'Argument with type \"{type(arg)}\" is not accepted, please read the docs.')\n","sourceCodeStart":713,"sourceCodeEnd":749,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/parser.py#L713-L749","documentation":"A dict passed to find_all() as an attribute filter contained a non-string key or a non-string value. scrapling requires flat Dict[str, str] — nested dicts or dicts with int/list values are rejected with a TypeError because they cannot be translated into a CSS attribute selector.","triggerScenarios":"find_all({'class': ['a', 'b']}), find_all({'data-count': 3}), or find_all({'attrs': {'id': 'x'}}).","commonSituations":"Feeding unnormalized JSON/scraped data directly as attribute filters; assuming BeautifulSoup-style tolerance where find_all(attrs={'x': 1}) works.","solutions":["Stringify values: {k: str(v) for k, v in attrs.items()}.","For multi-valued attributes like class lists, use a regex Pattern as a separate arg: find_all(re.compile(r'\\b(a|b)\\b')) or loop over each value.","Unnest nested dicts into separate find_all calls, one attribute set at a time."],"exampleFix":"# before\nselector.find_all({'data-count': 3})  # TypeError\n\n# after\nselector.find_all({'data-count': '3'})","handlingStrategy":"type-guard","validationCode":"attrs = {k: str(v) for k, v in attrs.items() if isinstance(k, str)}\nselector.find_all(attrs)","typeGuard":"def is_str_str_dict(d) -> bool:\n    return isinstance(d, dict) and all(\n        isinstance(k, str) and isinstance(v, str) for k, v in d.items()\n    )","tryCatchPattern":null,"preventionTips":["Normalize attribute-filter dicts from JSON/config by stringifying all values.","Never nest attribute dicts; split into separate calls."],"tags":["find-all","validation","dict","type-error"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}