{"record":{"id":"4a192ca823f46827","repo":"unslothai/unsloth","slug":"invalid-label-must-be-alphanumeric-dash-undersc","errorCode":null,"errorMessage":"Invalid {label}: must be alphanumeric/dash/underscore only","messagePattern":"Invalid (.+?): must be alphanumeric/dash/underscore only","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"studio/backend/routes/data_recipe/seed.py","lineNumber":70,"sourceCode":"\nlogger = get_logger(__name__)\nrouter = APIRouter()\n\nDATA_EXTS = (\".parquet\", \".jsonl\", \".json\", \".csv\")\nDEFAULT_SPLIT = \"train\"\nLOCAL_UPLOAD_EXTS = {\".csv\", \".json\", \".jsonl\"}\nUNSTRUCTURED_ALLOWED_EXTS = {\".pdf\", \".docx\", \".txt\", \".md\"}\nSEED_UPLOAD_DIR = seed_uploads_root()\nUNSTRUCTURED_UPLOAD_ROOT = unstructured_uploads_root()\n_SAFE_ID_RE = re.compile(r\"^[a-zA-Z0-9_-]+$\")\n# Frontend-generated upload namespace (UUID4 hex). Legacy node ids (n1, ...)\n# never match: those directories can be shared by several recipes.\n_UPLOAD_UID_RE = re.compile(r\"^[0-9a-f]{32}$\")\n\n\ndef _validate_safe_id(value: str, label: str) -> str:\n    if not value or not _SAFE_ID_RE.match(value):\n        raise HTTPException(400, f\"Invalid {label}: must be alphanumeric/dash/underscore only\")\n    return value\n\n\ndef _serialize_preview_value(value: Any) -> Any:\n    return to_preview_jsonable(value)\n\n\ndef _serialize_preview_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:\n    return [\n        {str(key): _serialize_preview_value(value) for key, value in row.items()} for row in rows\n    ]\n\n\ndef _normalize_optional_text(value: str | None) -> str | None:\n    if value is None:\n        return None\n    trimmed = value.strip()\n    return trimmed if trimmed else None","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/data_recipe/seed.py#L52-L88","documentation":"HTTP 400 raised by _validate_safe_id in the seed upload routes when a block_id or file_id fails the regex ^[a-zA-Z0-9_-]+$ (empty string or any character outside letters, digits, dash, underscore). The guard exists because these ids are interpolated into filesystem paths under the upload root, so it doubles as path-traversal protection.","triggerScenarios":"POST /seed/upload-unstructured-file with block_id containing a dot, slash, space, or unicode char; DELETE /seed/unstructured-file/{block_id}/{file_id} with a URL-encoded path segment like %2e%2e; any seed endpoint taking block_id/file_id form or path params with invalid characters.","commonSituations":"Frontend sends a node id like 'n.1' or a namespaced id with a colon; user-supplied filename used as an id; proxy stripping/decoding URL components; test fixtures using arbitrary strings.","solutions":["Generate ids client-side from [a-zA-Z0-9_-] only (the frontend upload namespace is a UUID4 hex, which always passes).","Sanitize before sending: strip or replace invalid characters in block_id/file_id.","If you intended a legacy node id (n1, n2), keep it alphanumeric — no dots or slashes.","Check the actual request payload in devtools to find which field carries the bad character."],"exampleFix":"// before\nconst blockId = `block/${recipeId}:${nodeId}`; // slashes/colons rejected\n\n// after\nconst blockId = crypto.randomUUID().replace(/-/g, ''); // 32-hex uid, always valid","handlingStrategy":"validation","validationCode":"const SAFE_ID = /^[a-zA-Z0-9_-]+$/;\nfunction isValidSafeId(v) { return typeof v === 'string' && SAFE_ID.test(v); }\nif (!isValidSafeId(blockId)) throw new Error(`invalid block_id: ${blockId}`);","typeGuard":"const SAFE_ID_RE = /^[a-zA-Z0-9_-]+$/;\nfunction isSafeId(v: unknown): v is string {\n  return typeof v === 'string' && v.length > 0 && SAFE_ID_RE.test(v);\n}","tryCatchPattern":"Wrap the upload/inspect fetch; on 400 with 'Invalid ... alphanumeric' in the detail, surface a form error naming the offending field instead of retrying.","preventionTips":["Always generate block ids as UUID4 hex client-side.","Never build ids from user-typed filenames.","Run the safe-id regex in a shared client util before any seed API call."],"tags":["validation","upload","path-traversal","http-400"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}