tonhowtf/omniget · error

vimeo:is_a_collection

vimeo:is_a_collection

Error message

vimeo:is_a_collection

What it means

vimeo:is_a_collection is raised by `private` when the URL parses successfully but points to a collection (showcase, album, channel, or user profile) rather than a single video. The private-video path downloads exactly one video, so collection targets are rejected with this dedicated code.

Solutions

  1. Route the request to `list` (enumerate) or `backup` (batch download) instead of `private`.
  2. Ensure the URL is a single video: https://vimeo.com/<numeric-id> or /<id>/<hash>.
  3. In the caller, use the parsed target kind (video vs showcase/album/channel/user) to pick the correct API before invoking.
  4. Fix UI validation so collection URLs cannot be submitted in the private-video form.

Example fix

// before
await vimeoPrivate({ url: "https://vimeo.com/showcase/9911" });
// after
if (/vimeo\.com\/(showcase|album|channels)\//.test(url) || !/\d+/.test(url.split("/").pop() ?? "")) {
  await vimeoList({ url });
} else {
  await vimeoPrivate({ url });
}
Defensive patterns

Strategy: validation

Validate before calling

const COLLECTION_RE = /vimeo\.com\/(showcase|album|channels)\//;
function isProfileUrl(u) {
  const s = (u ?? "").trim();
  return /^https:\/\/vimeo\.com\/[a-zA-Z][\w-]*\/?$/.test(s);
}
if (COLLECTION_RE.test(opts.url) || isProfileUrl(opts.url)) {
  return vimeoList({ url: opts.url }); // route to collection flow instead
}

Type guard

function isSingleVideoTarget(u) {
  return /^(https:\/\/vimeo\.com\/\d+|\d+)(\/[a-z0-9]+)?$/.test((u ?? "").trim());
}

Try / catch

try {
  await vimeoPrivate({ url });
} catch (e) {
  if (String(e).includes("vimeo:is_a_collection")) {
    showError("That URL is a collection — use batch download instead");
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `private(opts)` with opts.url such as https://vimeo.com/showcase/123456, /album/<id>, /channels/<name>, or /<username> — anything where `target.is_collection()` is true.

Common situations: User pastes their portfolio/profile URL into the single-video field; mixing up the 'download collection' and 'download private video' features in the UI; automated routing not distinguishing video ids from usernames.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/1b3b23bac063d2a3. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/vimeo.rs:1095

        kind: target.kind().to_string(),
        title,
        dest: dest.to_string_lossy().to_string(),
        total,
        ok,
        skipped,
        failed,
        items,
        used_session,
        command: last_command,
    })
}

/// Um vídeo privado ou unlisted. O hash já vai na URL; a senha, quando existe,
/// vai em `--video-password` e nunca sai daqui.
pub async fn private(opts: &PrivateOptions, progress: &ProgressFn) -> Result<PrivateResult> {
    let target = parse_target(&opts.url).ok_or_else(|| anyhow!("vimeo:bad_url"))?;
    if target.is_collection() {
        return Err(anyhow!("vimeo:is_a_collection"));
    }
    let (id, unlisted_hash) = match &target {
        Target::Video { id, hash } => (id.clone(), hash.is_some()),
        _ => (String::new(), false),
    };
    let url = target.canonical_url();
    let dest = PathBuf::from(&opts.dest);
    std::fs::create_dir_all(&dest)?;
    let bin = ytdlp_bin().await?;
    let ffmpeg = crate::core::dependencies::find_tool("ffmpeg").await;
    let cookies = cookie_file(opts.session_netscape.as_deref());
    let used_session = cookies.is_some();
    let secrets = secret_refs(opts.video_password.as_ref(), None);
    let used_password = opts
        .video_password
        .as_deref()
        .map(|p| !p.is_empty())
        .unwrap_or(false);

View on GitHub (pinned to 8600b91f42)