Hmbown/CodeWhale · error · TypeError
parallel(): expected an array of thunks
Error message
parallel(): expected an array of thunks
What it means
load_credentials found the Codex auth file (auth.json under the Codex home, located via an ExternalCredentialReadGrant) but serde could not deserialize it into the CodexAuthFile shape. The function deliberately distinguishes 'file absent' (Ok(None) -> silent re-auth) from 'file present but malformed' (this Err), because a corrupt credential file usually means the on-disk token store was truncated, overwritten by another tool, or hand-edited. The path is quoted in the message so the offending file can be found without leaking its contents.
Source
Thrown at crates/workflow-js/src/vm.rs:1073
const isFatalTaskError = (err) => {
const text = taskErrorText(err);
return text.includes("responseSchema") || text.includes("run cancelled");
};
globalThis.task = async (opts) => {
if (opts === null || typeof opts !== "object") {
throw new TypeError("task(): expected an options object");
}
const envelope = JSON.parse(await hostTask(JSON.stringify(opts)));
if (envelope.error !== undefined) {
throw new Error(envelope.error);
}
return envelope.value;
};
globalThis.parallel = (thunks) => {
if (!Array.isArray(thunks)) {
throw new TypeError("parallel(): expected an array of thunks");
}
if (thunks.length > MAX_ITEMS) {
throw new Error("parallel(): max " + MAX_ITEMS + " items per call");
}
return Promise.all(thunks.map((thunk) => {
try {
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;
}
}));
};View on GitHub (pinned to 8880682c63)
Solutions
- Re-authenticate: delete or move the quoted auth.json aside and run the Codex OAuth login flow again to regenerate it.
- Check what is actually in the quoted file: it must be valid JSON with the expected token structure; fix or remove stray content.
- Verify CODEX_HOME (or the equivalent env/config) points at the intended profile directory and is not shared with an incompatible tool version.
- If the file was truncated by a crash, restore from backup or simply re-login — refresh tokens cannot be recovered from a corrupt file.
Example fix
# before # auth.json is corrupt -> every Codex request fails with # 'Codex credential file ... is not valid credential JSON' # after mv "$(quoted auth.json path)" auth.json.broken codewhale oauth login codex # regenerates auth.json
Defensive patterns
Strategy: try-catch
Validate before calling
let contents = std::fs::read_to_string(&auth_path)?;
if serde_json::from_str::<serde_json::Value>(&contents).is_err() {
// regenerate instead of proceeding: re-run the OAuth login flow
} Type guard
fn is_valid_auth_json(raw: &str) -> bool {
serde_json::from_str::<serde_json::Value>(raw).is_ok()
} Try / catch
match load_credentials(&grant) {
Ok(Some(creds)) => creds,
Ok(None) => start_oauth_login().await?,
Err(e) if e.to_string().contains("not valid credential JSON") => {
backup_and_remove(&grant.path())?; // move the corrupt file aside
start_oauth_login().await? // then re-authenticate
}
Err(e) => return Err(e),
} Prevention
- Treat any malformed auth.json as fatal-for-that-provider and immediately re-login rather than retrying requests.
- Point CODEX_HOME at a dedicated profile directory not shared with other tool versions.
- Never hand-edit token files; write tokens only through the OAuth flow.
- Watch for proxies or disk-full conditions corrupting token refresh writes.
When it happens
Trigger: Any attempt to use Codex OAuth credentials: load_credentials parses auth.json and serde_json::from_str fails — e.g. truncated file (crash during write), BOM/HTML error page saved instead of JSON, or a schema the CodexAuthFile deserializer does not accept.
Common situations: Disk-full or process kill during a token refresh leaving a half-written auth.json; CODEX_HOME pointing at a directory managed by an incompatible Codex CLI version; a proxy capturing the token endpoint and writing an error body; manual editing of the file.
Related errors
- Kimi CLI credential import is unsupported. Codewhale does no
- OpenAI Codex OAuth credentials are unavailable. Codewhale c
- xAI OAuth credentials not found. Options: 1. Run `codewhale
- budget document_kind must be {BUDGET_KIND}
- budget fixture no longer matches the frozen workload
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/fe525006c51340e0.
Report an issue: GitHub.