Hmbown/CodeWhale · error · Error
pipeline(): max ${MAX_ITEMS} items per call
Error message
pipeline(): max ${MAX_ITEMS} items per call What it means
validate_staged's first check is to locate the manifest of the freshly staged tree: resolve_manifest_path accepts only plugin.json, kimi.plugin.json (Kimi Code compatibility), or plugin.toml at the staged root, in that precedence. If none of the three exists at the top level, the staged copy is rejected before validation or copying proceeds. This usually means the source's manifest sat in a subdirectory that got flattened, dropped, or renamed on the way into staging.
Source
Thrown at crates/workflow-js/src/vm.rs:1098
return Promise.resolve(typeof thunk === "function" ? thunk() : thunk).catch((err) => {
if (isFatalTaskError(err)) throw err;
hostLog("parallel(): dropped a failed slot as null: " + String((err && err.message) || err));
return null;
});
} catch (err) {
if (isFatalTaskError(err)) return Promise.reject(err);
hostLog("parallel(): dropped a failed slot as null: " + String((err && err.message) || err));
return null;
}
}));
};
globalThis.pipeline = (items, ...stages) => {
if (!Array.isArray(items)) {
throw new TypeError("pipeline(): expected an array of items");
}
if (items.length > MAX_ITEMS) {
throw new Error("pipeline(): max " + MAX_ITEMS + " items per call");
}
return Promise.all(items.map(async (item, index) => {
let value = item;
for (const stage of stages) {
try {
value = await stage(value, item, index);
} catch (err) {
if (isFatalTaskError(err)) throw err;
hostLog("pipeline(): dropped item " + index + " as null: " + String((err && err.message) || err));
return null;
}
}
return value;
}));
};
globalThis.log = (message) => {
hostLog(typeof message === "string" ? message : (JSON.stringify(message) ?? String(message)));View on GitHub (pinned to 8880682c63)
Solutions
- Re-check the staged/root layout: the bundle directory passed to install must directly contain plugin.json, kimi.plugin.json, or plugin.toml.
- If your archive has a wrapper folder, point the install at <extract-dir>/<wrapper>/ so the manifest lands at the root.
- If you maintain the bundle, name the manifest one of the three supported files and keep it at the root; do not symlink it.
Example fix
# before: bundle root has the manifest nested bundle/ src/plugin.json <- staged root 'bundle' has no manifest -> error # after bundle/ plugin.json src/...
Defensive patterns
Strategy: validation
Validate before calling
if crate::plugins::agent_plugin::resolve_manifest_path(source).is_none() {
anyhow::bail!(
"{} has no plugin.json/kimi.plugin.json/plugin.toml at its root — pass the bundle directory itself",
source.display()
);
} Type guard
fn is_plugin_bundle(dir: &Path) -> bool {
crate::plugins::agent_plugin::resolve_manifest_path(dir).is_some()
} Try / catch
match validate_staged(&staged_path) {
Ok((name, hash)) => (name, hash),
Err(e) if e.to_string().contains("staged bundle has no plugin.json") =>
return Err(e).context("bundle layout must keep a root manifest through staging"),
Err(e) => return Err(e),
} Prevention
- Point installs at the directory that directly contains plugin.json, not a checkout or archive root.
- Do not filter plugin.json/kimi.plugin.json/plugin.toml out of archives or copy scripts.
- Never symlink the manifest; ship it as a regular file at the bundle root.
- Check staged bundles (not just sources) with resolve_manifest_path before validation.
When it happens
Trigger: Installing a bundle whose manifest lives one level below the root (repo-style layout); packaging pipelines or tarball prefix-stripping that moves/removes plugin.json; a bundle shipping only an unsupported manifest filename (e.g. manifest.json); a manifest that is a symlink or non-regular file, which is_file() rejects.
Common situations: git clone of a monorepo where the plugin lives in a subdirectory; zips with wrapper folders whose stripping logic removes the wrong level; .gitignore-style copy filters excluding the manifest; bundles authored for other plugin ecosystems with different manifest names.
Related errors
- parallel(): max ${MAX_ITEMS} items per call
- Thread summaries require the runtime bearer token.
- Restore points require the runtime bearer token.
- Restore points returned HTTP ${response.statusCode}.
- pipeline(): expected an array of items
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/e5b2f971a813638c.
Report an issue: GitHub.