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

  1. Re-authenticate: delete or move the quoted auth.json aside and run the Codex OAuth login flow again to regenerate it.
  2. Check what is actually in the quoted file: it must be valid JSON with the expected token structure; fix or remove stray content.
  3. Verify CODEX_HOME (or the equivalent env/config) points at the intended profile directory and is not shared with an incompatible tool version.
  4. 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

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


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/fe525006c51340e0. Report an issue: GitHub.