{"record":{"id":"fe525006c51340e0","repo":"Hmbown/CodeWhale","slug":"parallel-expected-an-array-of-thunks","errorCode":null,"errorMessage":"parallel(): expected an array of thunks","messagePattern":"parallel\\(\\): expected an array of thunks","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/workflow-js/src/vm.rs","lineNumber":1073,"sourceCode":"  const isFatalTaskError = (err) => {\n    const text = taskErrorText(err);\n    return text.includes(\"responseSchema\") || text.includes(\"run cancelled\");\n  };\n\n  globalThis.task = async (opts) => {\n    if (opts === null || typeof opts !== \"object\") {\n      throw new TypeError(\"task(): expected an options object\");\n    }\n    const envelope = JSON.parse(await hostTask(JSON.stringify(opts)));\n    if (envelope.error !== undefined) {\n      throw new Error(envelope.error);\n    }\n    return envelope.value;\n  };\n\n  globalThis.parallel = (thunks) => {\n    if (!Array.isArray(thunks)) {\n      throw new TypeError(\"parallel(): expected an array of thunks\");\n    }\n    if (thunks.length > MAX_ITEMS) {\n      throw new Error(\"parallel(): max \" + MAX_ITEMS + \" items per call\");\n    }\n    return Promise.all(thunks.map((thunk) => {\n      try {\n        return Promise.resolve(typeof thunk === \"function\" ? thunk() : thunk).catch((err) => {\n          if (isFatalTaskError(err)) throw err;\n          hostLog(\"parallel(): dropped a failed slot as null: \" + String((err && err.message) || err));\n          return null;\n        });\n      } catch (err) {\n        if (isFatalTaskError(err)) return Promise.reject(err);\n        hostLog(\"parallel(): dropped a failed slot as null: \" + String((err && err.message) || err));\n        return null;\n      }\n    }));\n  };","sourceCodeStart":1055,"sourceCodeEnd":1091,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/workflow-js/src/vm.rs#L1055-L1091","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\n# auth.json is corrupt -> every Codex request fails with\n# 'Codex credential file ... is not valid credential JSON'\n\n# after\nmv \"$(quoted auth.json path)\" auth.json.broken\ncodewhale oauth login codex   # regenerates auth.json","handlingStrategy":"try-catch","validationCode":"let contents = std::fs::read_to_string(&auth_path)?;\nif serde_json::from_str::<serde_json::Value>(&contents).is_err() {\n    // regenerate instead of proceeding: re-run the OAuth login flow\n}","typeGuard":"fn is_valid_auth_json(raw: &str) -> bool {\n    serde_json::from_str::<serde_json::Value>(raw).is_ok()\n}","tryCatchPattern":"match load_credentials(&grant) {\n    Ok(Some(creds)) => creds,\n    Ok(None) => start_oauth_login().await?,\n    Err(e) if e.to_string().contains(\"not valid credential JSON\") => {\n        backup_and_remove(&grant.path())?;   // move the corrupt file aside\n        start_oauth_login().await?           // then re-authenticate\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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."],"tags":["rust","oauth","codex","credentials","json","configuration"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}