oxc-project/oxc · warning · OxcDiagnostic

Unexpected sync method: '{property_name}'.

Error message

Unexpected sync method: '{property_name}'.

What it means

Diagnostic from oxlint rule node/no-sync (style). It flags calls to *Sync-suffixed methods (fs.readFileSync, child_process.execSync, ...) inside functions: synchronous IO blocks Node's single-threaded event loop for the whole call, stalling every concurrent request. The message interpolates the exact property name being called. Options: allowAtRootLevel permits sync calls in top-level script code, and ignores allowlists method names.

Source

Thrown at crates/oxc_linter/src/rules/node/no_sync.rs:19

use rustc_hash::FxHashSet;
use schemars::JsonSchema;
use serde::Deserialize;

use oxc_ast::{AstKind, ast::Expression};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use oxc_str::CompactStr;

use crate::{
    AstNode,
    ast_util::get_enclosing_function,
    context::LintContext,
    rule::{DefaultRuleConfig, Rule},
};

fn no_sync_diagnostic(span: Span, property_name: &str) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!("Unexpected sync method: '{property_name}'.")).with_label(span)
}

#[derive(Debug, Default, Clone, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
struct NoSyncConfig {
    /// Whether synchronous methods should be allowed at the top level of a file.
    allow_at_root_level: bool,
    /// Function names to ignore.
    ignores: FxHashSet<CompactStr>,
}

#[derive(Debug, Default, Clone, Deserialize)]
pub struct NoSync(Box<NoSyncConfig>);

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Disallows synchronous methods from being called in Node.js code.

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Switch to the async API and await it: 'const d = await fs.promises.readFile(p, "utf8")'
  2. Keep sync calls confined to one-shot CLI/bootstrap code and enable { "allowAtRootLevel": true } for those directories (or scope the rule to server code only)
  3. Silence deliberate cases via the ignores option with the exact method names: "node/no-sync": ["error", { "ignores": ["readFileSync"] }]
  4. Move expensive load-time work (parsing big JSON, requiring heavy modules) into startup, not per-request paths

Example fix

// before
app.get('/report', (req, res) => {
  const data = fs.readFileSync('./data.json', 'utf8'); // blocks the event loop
  res.send(data);
});

// after
app.get('/report', async (req, res) => {
  const data = await fs.promises.readFile('./data.json', 'utf8');
  res.send(data);
});
Defensive patterns

Strategy: validation

Validate before calling

// .oxlintrc.json — strict for server code
"rules": { "node/no-sync": "error" }
// permissive for scripts/
"overrides": [{
  "files": ["scripts/**"],
  "rules": { "node/no-sync": ["error", { "allowAtRootLevel": true, "ignores": ["readFileSync"] }] }
}]

npx oxlint -c .oxlintrc.json --deny-warning .

Prevention

When it happens

Trigger: A member-expression call whose property name ends in 'Sync' — the diagnostic message embeds the property, e.g. "Unexpected sync method: 'readFileSync'." — located inside a function (get_enclosing_function finds a function parent). Not reported when allowAtRootLevel is true and the call sits at file top level, or when the callee matches the ignores set.

Common situations: One-off scripts copy-pasted into Express/Fastify handlers where they now stall concurrency; build-tool utilities reused at runtime; migrations from eslint-plugin-n where the ignores allowlist must be re-provided in camelCase JSON; sync JSON requires of big files on the request path.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/24d1ef4f2aa9d07c. Report an issue: GitHub.