{"record":{"id":"8bacf18d51930e5e","repo":"calesthio/OpenMontage","slug":"fetch-failed-r-status-url","errorCode":null,"errorMessage":"fetch failed ${r.status}: ${url}","messagePattern":"fetch failed (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ink-theater/mocap/add-motion.mjs","lineNumber":31,"sourceCode":"// only needs an alias added to bvh2clip.mjs's ALIAS table.\nimport { writeFileSync, readFileSync, readdirSync, existsSync, mkdirSync } from \"node:fs\";\nimport { execFileSync } from \"node:child_process\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst [, , name, source, category = \"action\", desc = \"\"] = process.argv;\nif (!name || !source) { console.error(\"usage: node add-motion.mjs <name> <source> [category] [description]\"); process.exit(1); }\n\nasync function resolveBvh(src) {\n  const tmp = join(here, \"src\"); mkdirSync(tmp, { recursive: true });\n  const out = join(tmp, name + \".bvh\");\n  if (existsSync(src)) { writeFileSync(out, readFileSync(src)); return out; }\n  let url = src;\n  const CMU = \"https://raw.githubusercontent.com/una-dinosauria/cmu-mocap/master/data\";\n  if (/^\\d{2,3}_\\d+$/.test(src)) { const subj = src.split(\"_\")[0].padStart(3, \"0\"); url = `${CMU}/${subj}/${src}.bvh`; }\n  else if (/^\\d+\\/\\d{2,3}_\\d+$/.test(src)) { url = `${CMU}/${src}.bvh`; }\n  const r = await fetch(url); if (!r.ok) throw new Error(`fetch failed ${r.status}: ${url}`);\n  writeFileSync(out, Buffer.from(await r.arrayBuffer())); return out;\n}\n\nconst bvh = await resolveBvh(source);\nexecFileSync(\"node\", [join(here, \"bvh2clip.mjs\"), bvh, join(here, \"clips\", name + \".json\"), \"--fps\", \"30\", \"--max\", \"180\", \"--name\", name], { stdio: \"inherit\" });\n\nconst clipsDir = join(here, \"clips\");\nconst clips = {};\nfor (const f of readdirSync(clipsDir).filter((f) => f.endsWith(\".json\"))) clips[f.replace(/\\.json$/, \"\")] = JSON.parse(readFileSync(join(clipsDir, f), \"utf8\"));\nwriteFileSync(join(here, \"clips.js\"), \"window.INK_CLIPS=\" + JSON.stringify(clips) + \";\");\n\nconst catPath = join(here, \"catalog.json\");\nconst cat = existsSync(catPath) ? JSON.parse(readFileSync(catPath, \"utf8\")) : [];\nconst entry = { name, category, desc: desc || name, frames: clips[name].frameCount, source };\nconst i = cat.findIndex((c) => c.name === name);\nif (i >= 0) cat[i] = entry; else cat.push(entry);\ncat.sort((a, b) => a.name.localeCompare(b.name));\nwriteFileSync(catPath, JSON.stringify(cat, null, 2));","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/ink-theater/mocap/add-motion.mjs#L13-L49","documentation":"Thrown when duration cannot be converted to an integer at all — int(value) raises TypeError (None, list, dict) or ValueError (\"abc\", \"4s\", \"\"). The tool wraps that failure in a consistent ValueError describing the accepted range. This is the type/format gate before any range checking happens.","triggerScenarios":"duration=null/None, duration=\"10s\", duration=\"\", duration=[10], or any string that is not a clean integer passed to seedance_ark's duration parameter.","commonSituations":"LLM-generated arguments with units attached (\"8s\"), nulls from optional JSON fields forwarded unfiltered, duration copied from a UI text field containing whitespace or unit suffixes.","solutions":["Pass duration as a plain integer (4-15), -1, or \"auto\".","Strip units and whitespace from user-supplied strings before the call (e.g. parse \"8s\" to 8).","Default null duration to \"auto\" instead of forwarding None."],"exampleFix":"# before\ninputs = {\"duration\": \"8s\"}\n# after\nimport re\nm = re.search(r\"\\d+\", \"8s\")\ninputs = {\"duration\": int(m.group()) if m else \"auto\"}","handlingStrategy":"validation","validationCode":"def normalize_duration(v, default=\"auto\"):\n    if v is None:\n        return default\n    s = str(v).strip()\n    if s == \"auto\":\n        return \"auto\"\n    digits = s.rstrip(\"s sec\").strip()\n    try:\n        return int(digits)\n    except ValueError:\n        return default","typeGuard":"def is_parseable_duration(v) -> bool:\n    if v is None or isinstance(v, bool):\n        return False\n    if isinstance(v, int):\n        return True\n    if isinstance(v, str):\n        s = v.strip()\n        return s == \"auto\" or (s.lstrip('-').isdigit() and len(s) < 8)\n    return False","tryCatchPattern":"try:\n    tool.run(inputs)\nexcept ValueError as e:\n    if \"duration must be an integer\" in str(e):\n        inputs[\"duration\"] = \"auto\"\n        tool.run(inputs)\n    else:\n        raise","preventionTips":["Reject or normalize duration at input boundaries (strip units, nulls, and containers).","Keep duration typed as int|string in your own schemas and coerce early."],"tags":["python","validation","duration","type-error","seedance"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}