{"record":{"id":"522576655477d5d7","repo":"D4Vinci/Scrapling","slug":"selector-class-needs-html-content-or-root-argumen","errorCode":null,"errorMessage":"Selector class needs HTML content, or root arguments to work","messagePattern":"Selector class needs HTML content, or root arguments to work","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapling/parser.py","lineNumber":119,"sourceCode":"        It's an old issue with lxml, see `this entry <https://bugs.launchpad.net/lxml/+bug/736708>`\n\n        :param content: HTML content as either string or bytes.\n        :param url: It allows storing a URL with the HTML data for retrieving later.\n        :param encoding: The encoding type that will be used in HTML parsing, default is `UTF-8`\n        :param huge_tree: Enabled by default, should always be enabled when parsing large HTML documents. This controls\n             the libxml2 feature that forbids parsing certain large documents to protect from possible memory exhaustion.\n        :param root: Used internally to pass etree objects instead of text/body arguments, it takes the highest priority.\n            Don't use it unless you know what you are doing!\n        :param keep_comments: While parsing the HTML body, drop comments or not. Disabled by default for obvious reasons\n        :param keep_cdata: While parsing the HTML body, drop cdata or not. Disabled by default for cleaner HTML.\n        :param adaptive: Globally turn off the adaptive feature in all functions, this argument takes higher\n            priority over all adaptive related arguments/functions in the class.\n        :param storage: The storage class to be passed for adaptive functionalities, see ``Docs`` for more info.\n        :param storage_args: A dictionary of ``argument->value`` pairs to be passed for the storage class.\n            If empty, default values will be used.\n        \"\"\"\n        if root is None and content is None:\n            raise ValueError(\"Selector class needs HTML content, or root arguments to work\")\n\n        self.url = url\n        self._raw_body: str | bytes = \"\"\n        self.encoding = encoding\n        self.__keep_cdata = keep_cdata\n        self.__huge_tree_enabled = huge_tree\n        self.__keep_comments = keep_comments\n        # For selector stuff\n        self.__text: Optional[TextHandler] = None\n        self.__attributes: Optional[AttributesHandler] = None\n        self.__tag: Optional[str] = None\n        self._storage: Optional[StorageSystemMixin] = None\n        if root is None:\n            body: str | bytes\n            if isinstance(content, str):\n                body = content.strip().replace(\"\\x00\", \"\") or \"<html/>\"\n            elif isinstance(content, bytes):\n                body = content.replace(b\"\\x00\", b\"\")","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/parser.py#L101-L137","documentation":"ValueError from the Selector (`__init__` in scrapling/parser.py): a Selector must be constructed from HTML content or a prebuilt lxml root element. When both `content` and `root` are None the class has nothing to parse and refuses construction immediately.","triggerScenarios":"Selector() with no args; Selector(content=None) when a fetch returned nothing (e.g. empty page or a failed request piped straight in); Selector(root=None, content=None) after a conversion step returned None.","commonSituations":"Chaining Selector(response.content) where response.content is None on error responses; building Selectors in a loop where some items have no body; refactoring code that used to pass text= (old API) so content is accidentally dropped.","solutions":["Pass HTML: Selector('<html>...</html>') or Selector(content=response.content or '<html/>').","Guard upstream: if not content: skip/handle the empty case before constructing.","If you already have an lxml element, pass it as root=element."],"exampleFix":"# before\nsel = Selector(content=response.content)  # content is None\n\n# after\nbody = response.content or '<html/>'\nif body is None:\n    return []\nsel = Selector(content=body)","handlingStrategy":"validation","validationCode":"body = response.content if response is not None else None\nif body is None and root is None:\n    raise ValueError('nothing to parse: fetch returned no content')\nsel = Selector(content=body, root=root)","typeGuard":"def has_parse_input(content, root) -> bool:\n    return content is not None or root is not None","tryCatchPattern":"try:\n    sel = Selector(content=body)\nexcept ValueError as e:\n    if \"needs HTML content\" in str(e):\n        sel = Selector(content='<html/>')  # explicit empty document\n    else:\n        raise","preventionTips":["Check fetch results for None/empty bodies before constructing Selectors.","Default empty pages to '<html/>' deliberately if you must parse on.","Keep parameter names straight: content for HTML, root for lxml elements."],"tags":["parser","selector","validation","constructor"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}