dmtrKovalenko/fff · error

scan progress returned null

Error message

scan progress returned null

What it means

ffiGetScanProgress received a success envelope from fff_get_scan_progress whose handle pointer is 0, meaning the native side returned no ScanProgress struct. The binding maps this to an error instead of defaulting the progress fields.

Solutions

  1. Stop polling scan progress once the instance is destroyed; guard the call with an aliveness check.
  2. Only call fff_get_scan_progress with the handle returned by instance creation, kept valid until teardown.
  3. Rebuild the native library if the error occurs on a live instance.

Example fix

// before
setInterval(() => ffiGetScanProgress(handle), 500); // keeps polling after destroy()
// after
setInterval(() => { if (handle !== null) ffiGetScanProgress(handle); }, 500);
Defensive patterns

Strategy: type-guard

Validate before calling

if (finderHandle === null) return { scannedFilesCount: 0, isScanning: false };

Type guard

function canPollProgress(finder) { return finder != null && finder.handle !== null; }

Try / catch

try { const p = await finder.getScanProgress(); } catch (e) { if (e.message.includes('scan progress returned null')) stopProgressPolling(); }

Prevention

When it happens

Trigger: Calling ffiGetScanProgress on a handle whose native instance no longer exists (already destroyed/invalid), or a native failure to allocate the progress struct while still reporting success.

Common situations: Polling scan progress from a timer after the finder was closed; polling before init completed with a stale handle; version-mismatched native binary.

Related errors


AI-assisted analysis of dmtrKovalenko/fff@7f8537e70f (2026-09-10). Data as JSON: /api/errors/1f6fe10beb10fc20. Report an issue: GitHub.

Appendix: source

Thrown at packages/fff-bun/src/ffi.ts:1352

}

// FffScanProgress { scanned_files_count: u64(8), is_scanning: bool(1), is_watcher_ready: bool(1), is_warmup_complete: bool(1) + pad }
const SP_COUNT = 0; // u64 (8)
const SP_SCANNING = 8; // bool (1)
const SP_WATCHER_READY = 9; // bool (1)
const SP_WARMUP_COMPLETE = 10; // bool (1)

/**
 * Get scan progress.
 */
export function ffiGetScanProgress(handle: NativeHandle): Result<ScanProgress> {
  const library = loadLibrary();
  const resultPtr = library.symbols.fff_get_scan_progress(handle);
  const envelope = readResultEnvelope(resultPtr);
  if (!("success" in envelope)) return envelope;

  if (envelope.handlePtr === 0) {
    return err("scan progress returned null");
  }

  const hp = asPtr(envelope.handlePtr);
  const result: ScanProgress = {
    scannedFilesCount: Number(read.u64(hp, SP_COUNT)),
    isScanning: read.u8(hp, SP_SCANNING) !== 0,
    isWatcherReady: read.u8(hp, SP_WATCHER_READY) !== 0,
    isWarmupComplete: read.u8(hp, SP_WARMUP_COMPLETE) !== 0,
  };
  library.symbols.fff_free_scan_progress(hp);
  return { ok: true, value: result };
}

/**
 * Wait for scan to complete.
 */
export function ffiWaitForScan(handle: NativeHandle, timeoutMs: number): Result<boolean> {
  const library = loadLibrary();

View on GitHub (pinned to 7f8537e70f)