calesthio/OpenMontage · error · Error
fetch failed ${r.status}: ${url}
Error message
fetch failed ${r.status}: ${url} What it means
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.
Source
Thrown at ink-theater/mocap/add-motion.mjs:31
// only needs an alias added to bvh2clip.mjs's ALIAS table.
import { writeFileSync, readFileSync, readdirSync, existsSync, mkdirSync } from "node:fs";
import { execFileSync } from "node:child_process";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const [, , name, source, category = "action", desc = ""] = process.argv;
if (!name || !source) { console.error("usage: node add-motion.mjs <name> <source> [category] [description]"); process.exit(1); }
async function resolveBvh(src) {
const tmp = join(here, "src"); mkdirSync(tmp, { recursive: true });
const out = join(tmp, name + ".bvh");
if (existsSync(src)) { writeFileSync(out, readFileSync(src)); return out; }
let url = src;
const CMU = "https://raw.githubusercontent.com/una-dinosauria/cmu-mocap/master/data";
if (/^\d{2,3}_\d+$/.test(src)) { const subj = src.split("_")[0].padStart(3, "0"); url = `${CMU}/${subj}/${src}.bvh`; }
else if (/^\d+\/\d{2,3}_\d+$/.test(src)) { url = `${CMU}/${src}.bvh`; }
const r = await fetch(url); if (!r.ok) throw new Error(`fetch failed ${r.status}: ${url}`);
writeFileSync(out, Buffer.from(await r.arrayBuffer())); return out;
}
const bvh = await resolveBvh(source);
execFileSync("node", [join(here, "bvh2clip.mjs"), bvh, join(here, "clips", name + ".json"), "--fps", "30", "--max", "180", "--name", name], { stdio: "inherit" });
const clipsDir = join(here, "clips");
const clips = {};
for (const f of readdirSync(clipsDir).filter((f) => f.endsWith(".json"))) clips[f.replace(/\.json$/, "")] = JSON.parse(readFileSync(join(clipsDir, f), "utf8"));
writeFileSync(join(here, "clips.js"), "window.INK_CLIPS=" + JSON.stringify(clips) + ";");
const catPath = join(here, "catalog.json");
const cat = existsSync(catPath) ? JSON.parse(readFileSync(catPath, "utf8")) : [];
const entry = { name, category, desc: desc || name, frames: clips[name].frameCount, source };
const i = cat.findIndex((c) => c.name === name);
if (i >= 0) cat[i] = entry; else cat.push(entry);
cat.sort((a, b) => a.name.localeCompare(b.name));
writeFileSync(catPath, JSON.stringify(cat, null, 2));View on GitHub (pinned to 95e1c3d0ab)
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.
Example fix
# before
inputs = {"duration": "8s"}
# after
import re
m = re.search(r"\d+", "8s")
inputs = {"duration": int(m.group()) if m else "auto"} Defensive patterns
Strategy: validation
Validate before calling
def normalize_duration(v, default="auto"):
if v is None:
return default
s = str(v).strip()
if s == "auto":
return "auto"
digits = s.rstrip("s sec").strip()
try:
return int(digits)
except ValueError:
return default Type guard
def is_parseable_duration(v) -> bool:
if v is None or isinstance(v, bool):
return False
if isinstance(v, int):
return True
if isinstance(v, str):
s = v.strip()
return s == "auto" or (s.lstrip('-').isdigit() and len(s) < 8)
return False Try / catch
try:
tool.run(inputs)
except ValueError as e:
if "duration must be an integer" in str(e):
inputs["duration"] = "auto"
tool.run(inputs)
else:
raise Prevention
- 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.
When it happens
Trigger: duration=null/None, duration="10s", duration="", duration=[10], or any string that is not a clean integer passed to seedance_ark's duration parameter.
Common situations: 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.
Related errors
- ${res.status} ${url}
- path escapes project
- Checkpoint failed schema validation: {exc.message}
- media not found
- no poster frame available
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/8bacf18d51930e5e.
Report an issue: GitHub.