{"record":{"id":"f6612abadd989216","repo":"affaan-m/ECC","slug":"asset-name-must-be-a-simple-filename-stem-letters-digits","errorCode":null,"errorMessage":"asset name must be a simple filename stem (letters, digits, spaces, _.-)","messagePattern":"asset name must be a simple filename stem \\(letters, digits, spaces, _\\.-\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/mint3d.py","lineNumber":70,"sourceCode":"#\n# Generating a clean plate first costs ~$0.15 and is the difference between a\n# usable mesh and a discarded one.\nPLATE_RULES = (\n    \"A single isolated object centred on a plain neutral mid-grey seamless \"\n    \"background, filling most of the frame, evenly lit from three quarters, no \"\n    \"other objects, no text, no logos, no props, no shadows cast on the \"\n    \"backdrop, product-photography framing, sharp focus edge to edge, the whole \"\n    \"object visible with nothing cropped. Neutral colour, no colour grading.\"\n)\n\n\ndef _asset_name(name: str | None, prompt: str) -> str:\n    words = prompt.split()\n    asset = name if name is not None else \"prop_\" + (words[0] if words else \"lifted\")\n    if (not asset or asset in {\".\", \"..\"} or len(asset) > 120\n            or any(not (c.isalnum() or c in \"_-. \") for c in asset)\n            or asset != asset.strip()):\n        raise ValueError(\"asset name must be a simple filename stem (letters, digits, spaces, _.-)\")\n    return asset\n\n\ndef _check_outputs(sp, asset: str) -> None:\n    \"\"\"Reject existing artifacts for this stem before any billable work.\"\"\"\n    props = sp.dir / \"props\"\n    candidates = [props / f\"{asset}{suffix}\" for suffix in\n                  (\".glb\", \".json\", \"_plate.png\", \"_retopo.glb\")]\n    candidates += list(props.glob(f\"{asset}_part*.glb\"))\n    candidates += [sp.dir / \"turntables\" / asset,\n                   sp.dir / \"turntables\" / f\"{asset}.mp4\"]\n    for path in candidates:\n        if path.exists() or path.is_symlink():\n            raise FileExistsError(f\"asset output already exists; choose a new --name: {path}\")\n\n\ndef mint3d(\n    genre: str,","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/mint3d.py#L52-L88","documentation":"mint3d.py validates the derived asset name (from --name or the first prompt word) must be a non-empty filename stem of at most 120 chars using only alphanumerics, space, underscore, dash, dot, and must not be '.', '..', or have surrounding whitespace. Violations raise ValueError before any billable API calls.","triggerScenarios":"--name containing slashes, path components, unicode/special characters, leading/trailing whitespace, exceeding 120 chars, being empty, or being '.' or '..'; or a single-word prompt whose first word contains such characters when --name is omitted.","commonSituations":"Users pass names like 'my/asset', 'hero prop!' or quoted strings with trailing spaces; prompts starting with a punctuation-heavy word become an invalid derived name; names copied with a path prefix from elsewhere.","solutions":["Pass a clean --name using only letters, digits, spaces, '_', '-', '.'","Trim whitespace from the name before invoking","Strip path components: use the basename/stem instead of a full path","Simplify the prompt's first word or explicitly set --name so the prompt is not used for naming"],"exampleFix":"// before\npython mint3d.py noir --prompt \"rusty lamp\" --name \"assets/hero lamp!\"\n// after\npython mint3d.py noir --prompt \"rusty lamp\" --name \"hero_lamp\"","handlingStrategy":"validation","validationCode":"import re\nif name is not None and not re.fullmatch(r\"[\\w\\-. ]{1,120}\", name.strip()):\n    raise SystemExit(f\"invalid --name: {name!r}\")","typeGuard":"import re\n_VALID = re.compile(r\"[\\w\\-. ]{1,120}\")\ndef is_valid_asset_name(name: str) -> bool:\n    return name == name.strip() and bool(_VALID.fullmatch(name)) and name not in {\".\", \"..\"}","tryCatchPattern":"try:\n    mint3d(...)\nexcept ValueError as e:\n    print(f\"bad asset name: {e}\"); retry with sanitized name","preventionTips":["Sanitize --name with a regex before invoking","Strip path prefixes: use Path(name).stem","Avoid prompts whose first word is punctuation-heavy"],"tags":["validation","naming","cli"],"backgroundTag":"invalid-identifier-format","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}