affaan-m/ECC · error · ValueError
Draft name must be a plain file name
Error message
Draft name must be a plain file name
What it means
export_draft validates the draft name against ^[A-Za-z0-9][A-Za-z0-9_. -]{0,99}$ and rejects names ending with a dot. This keeps the name a single safe path component that works as a CapCut draft folder name on all platforms. Anything with path separators, leading special characters, control characters, or trailing dots is rejected.
Solutions
- Use only letters, digits, underscores, dots, spaces, and hyphens, starting with an alphanumeric character
- Strip or replace path components: name = Path(raw).stem or re.sub(r'[^A-Za-z0-9_. -]', '_', raw)
- Remove trailing dots: name = name.rstrip('.')
- Keep the name at or under 100 characters
Example fix
// before
name = str(Path(video).with_suffix('')) # may contain '/'
// after
name = Path(video).stem.replace(':', '-')[:100].rstrip('.') or 'draft' Defensive patterns
Strategy: validation
Validate before calling
import re
DRAFT_NAME_RE = re.compile(r'[A-Za-z0-9][A-Za-z0-9_. -]{0,99}')
def draft_name_ok(name):
return bool(name) and bool(DRAFT_NAME_RE.fullmatch(name)) and not name.endswith('.') Type guard
def is_safe_draft_name(name):
return isinstance(name, str) and bool(re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9_. -]{0,99}', name)) and not name.endswith('.') Try / catch
try:
export_draft(files, name=name)
except ValueError as e:
if 'plain file name' in str(e):
export_draft(files, name=sanitize(name)) Prevention
- Sanitize user input: strip path components and invalid characters before naming drafts
- Normalize names: re.sub(r'[^A-Za-z0-9_. -]', '_', raw).lstrip(string.punctuation)
- Strip trailing dots and cap length at 100 characters
- Test name sanitization with paths from both POSIX and Windows sources
When it happens
Trigger: Passing names containing '/' or '\\', leading dots/spaces/hyphens, names over 100 characters, names ending in '.', or empty/unicode names to export_draft.
Common situations: Deriving the draft name from a source filename including its directory; Windows-style paths leaking into the name; a user typing 'My Edit.' with a trailing period; names auto-generated with timestamps using ':' characters.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- At least one video is required
- application bundle differs from its bound evidence
- asset name must be a simple filename stem (letters, digits…
- cannot upload, file does not exist
- CapCut draft replacement is unsafe; choose a new name
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/82d13d8c2bafcdf7.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/media/capcut.py:61
def export_draft(
files,
drafts,
name,
width=1920,
height=1080,
fps=30,
overwrite=False,
cc=None,
probe=duration_of,
):
geometry(width, height, fps)
if overwrite:
raise ValueError("CapCut draft replacement is unsafe; choose a new name")
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_. -]{0,99}", name) or name.endswith(
"."
):
raise ValueError("Draft name must be a plain file name")
if not files:
raise ValueError("At least one video is required")
prepared = []
for filename in files:
path = Path(filename).resolve(strict=True)
duration = probe(path)
if not math.isfinite(duration) or duration <= 0:
raise ValueError(f"Invalid media duration: {path.name}")
prepared.append((str(path), duration))
if cc is None:
import pycapcut as cc
folder = cc.DraftFolder(str(drafts))
script = folder.create_draft(name, width, height, fps=fps, allow_replace=False)
script.add_track(cc.TrackType.video)
elapsed = 0
for filename, duration in prepared:
script.add_segment(
cc.VideoSegment(filename, cc.trange(f"{elapsed:.6f}s", f"{duration:.6f}s"))View on GitHub (pinned to 8321021c54)