{"record":{"id":"1f3c1d18d03a5877","repo":"denoland/deno","slug":"err-invalid-return-value","errorCode":"ERR_INVALID_RETURN_VALUE","errorMessage":"Expected boolean to be returned from the \"filter\" function but got ${determineSpecificType(value)}.","messagePattern":"Expected boolean to be returned from the \"filter\" function but got (.+?)\\.","errorType":"validation","errorClass":"NodeTypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_fs/cp/cp_sync.ts","lineNumber":27,"sourceCode":"} = core.loadExtScript(\"ext:deno_node/internal/errors.ts\");\nconst { op_node_cp_sync } = core.ops;\nconst { throwCpError } = core.loadExtScript(\"ext:deno_node/_fs/cp/cp.ts\");\n\nconst {\n  isPromise,\n} = core;\n\nfunction cpSyncFn(\n  src,\n  dest,\n  opts,\n) {\n  try {\n    if (opts.filter) {\n      // deno-lint-ignore deno-internal/prefer-primordials\n      const shouldCopy = opts.filter(src, dest);\n      if (isPromise(shouldCopy)) {\n        throw new ERR_INVALID_RETURN_VALUE(\"boolean\", \"filter\", shouldCopy);\n      }\n      if (!shouldCopy) return;\n    }\n\n    op_node_cp_sync(\n      src,\n      dest,\n      opts.dereference,\n      opts.recursive,\n      opts.force,\n      opts.errorOnExist,\n      opts.preserveTimestamps,\n      opts.verbatimSymlinks,\n      opts.mode ?? 0,\n      opts.filter,\n    );\n  } catch (err) {\n    if (typeof err?.os_errno === \"number\") {","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_fs/cp/cp_sync.ts#L9-L45","documentation":"fs.cpSync() requires its opts.filter callback to return a boolean synchronously. The polyfill explicitly checks isPromise(shouldCopy) and throws ERR_INVALID_RETURN_VALUE when the filter resolves asynchronously, because a synchronous copy loop cannot await a Promise. This matches Node.js behavior for the sync variant.","triggerScenarios":"fs.cpSync(src, dest, { filter: async (s, d) => await someCheck(s) }); or any filter that implicitly returns a Promise (calls an async helper, returns a thenable).","commonSituations":"Sharing one filter implementation between fs.cp (async, tolerates async filters) and fs.cpSync; refactoring a filter to call a database or network lookup making it async; copying filter code from an async codebase into a sync CLI tool.","solutions":["Make the filter fully synchronous: compute the answer with sync APIs (fs.statSync, string checks) and return a plain boolean","If the filter must stay async, switch to the asynchronous fs.promises.cp()/fs.cp() which can await it","If the filter awaits nothing real, remove the async keyword so it returns a boolean directly"],"exampleFix":"// before\nfs.cpSync(src, dest, {\n  filter: async (s, d) => (await fs.promises.stat(s)).size > 0,\n});\n\n// after\nfs.cpSync(src, dest, {\n  filter: (s) => fs.statSync(s).size > 0,\n});","handlingStrategy":"type-guard","validationCode":"if (typeof opts.filter === 'function') {\n  const probe = opts.filter(src, dest);\n  if (typeof probe.then === 'function') {\n    throw new TypeError('cpSync filter must be synchronous');\n  }\n}\nfs.cpSync(src, dest, opts);","typeGuard":"function isSyncFilter(filter) {\n  const v = filter('/probe', '/probe');\n  return typeof v === 'boolean' || (typeof v !== 'object' || v === null);\n}","tryCatchPattern":"try { fs.cpSync(src, dest, { filter }); } catch (e) { if (e.code === 'ERR_INVALID_RETURN_VALUE' && /filter/.test(e.message)) { /* switch to fs.promises.cp */ } else throw e; }","preventionTips":["Keep one sync filter and one async filter; never share across cp and cpSync","Lint for 'async' keyword in filters passed to *Sync APIs","Return an explicit boolean (not a truthy Promise) from sync filters"],"tags":["fs","cp","sync","filter","node-compat"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}