{"record":{"id":"19680efedbbcee4a","repo":"unslothai/unsloth","slug":"local-cache-path-contains-invalid-characters","errorCode":null,"errorMessage":"local cache path contains invalid characters","messagePattern":"local cache path contains invalid characters","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"studio/backend/models/training.py","lineNumber":270,"sourceCode":"        return v\n\n    @field_validator(\n        \"model_local_path\",\n        \"dataset_local_path\",\n        \"model_snapshot_path\",\n        \"dataset_snapshot_path\",\n    )\n    @classmethod\n    def _check_cache_local_path(cls, v: Optional[str]) -> Optional[str]:\n        if v is None:\n            return v\n        v = v.strip()\n        if not v:\n            return None\n        if len(v) > 4096:\n            raise ValueError(\"local cache path is too long (max 4096 chars)\")\n        if \"\\x00\" in v:\n            raise ValueError(\"local cache path contains invalid characters\")\n        if \"..\" in Path(v).parts or \"..\" in PureWindowsPath(v).parts:\n            raise ValueError(\"local cache path must not contain '..' segments\")\n        return v\n\n    @field_validator(\"train_split\", \"eval_split\")\n    @classmethod\n    def _check_split_name(cls, v: Optional[str]) -> Optional[str]:\n        if v is None:\n            return v\n        v = v.strip()\n        if not v:\n            return None\n        if len(v) > MAX_HF_DATASET_OPTION_LENGTH:\n            raise ValueError(f\"split name is too long (max {MAX_HF_DATASET_OPTION_LENGTH} chars)\")\n        if not valid_hf_dataset_split_instruction(v):\n            raise ValueError(\"split name contains invalid characters\")\n        return v\n","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/models/training.py#L252-L288","documentation":"Raised by the _check_cache_local_path field validator when the path string contains a NUL byte (\\x00). NUL cannot appear in a valid filesystem path on POSIX or Windows and would truncate the path at the OS boundary or raise EINVAL, so the validator rejects it with a clear message. This also serves as a payload-injection guard.","triggerScenarios":"POST a training start request with any of the four cache-path fields containing a literal \\x00 byte, e.g. from deserializing binary data into a string field or a truncated network read.","commonSituations":"Binary data mistakenly decoded into a path field; socket/HTTP payloads cut at a NUL; test fuzzers generating random bytes; data pipelines passing bytes where str is expected.","solutions":["Remove the NUL byte from the value and investigate how it got into a path field.","If reading from a socket/file, use proper length-prefixed or JSON framing instead of NUL-terminated reads.","Add a client-side check: if ('\\x00' in path) reject before submit."],"exampleFix":"# before\npath = raw_bytes.decode(\"utf-8\", \"replace\")  # may contain \\x00\n# after\npath = raw_bytes.decode(\"utf-8\").replace(\"\\x00\", \"\")  # or reject outright","handlingStrategy":"type-guard","validationCode":"def paths_nul_free(body: dict) -> bool:\n    return all(\"\\x00\" not in (body.get(f) or \"\") for f in PATH_FIELDS)","typeGuard":"function nulFree(p: string | undefined): boolean {\n  return !p?.includes('\\x00');\n}","tryCatchPattern":null,"preventionTips":["Never decode raw binary into path fields","Use length-prefixed or JSON framing for IPC instead of NUL-terminated strings"],"tags":["pydantic","validation","filesystem","nul-byte","security"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}