dmtrKovalenko/fff · error

grep returned null result

Error message

grep returned null result

What it means

parseGrepResult read a success envelope from fff_live_grep/fff_multi_grep but the result handle pointer is 0, so there is no grep result structure to parse. The binding refuses to return a fake empty result and surfaces this error instead.

Solutions

  1. Verify the finder instance is still alive before invoking grep (guard with ensureAlive).
  2. Rebuild the native library so the grep result ABI matches the bindings.
  3. If it persists on a valid instance with existing files, report as a native-side bug.

Example fix

// before
const r = ffiGrep(handle, pattern);
// after
if (handle === null) return err('finder destroyed');
const r = ffiGrep(handle, pattern);
Defensive patterns

Strategy: try-catch

Validate before calling

if (handle === null) return err('finder destroyed before grep');

Type guard

function hasNativeHandle(f) { return f != null && typeof f.handle === 'object' && f.handle !== null; }

Try / catch

try { const r = await finder.grep(pattern); } catch (e) { if (e.message.includes('grep returned null result')) return emptyGrepResult(); }

Prevention

When it happens

Trigger: Calling ffiGrep/ffiMultiGrep where the native grep call returns a success envelope with handlePtr === 0 — native allocation failure, destroyed instance, or an ABI/version mismatch in the grep result layout.

Common situations: Grep invoked right after destroy(); searching a directory that no longer exists with a mismatched native build; stale build artifact after upgrading.

Related errors


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

Appendix: source

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

  if (ctxAfterCount > 0) {
    match.contextAfter = readCStringArray(read.ptr(pp, GM_CTX_AFTER), ctxAfterCount);
  }
  if (read.u8(pp, GM_IS_DEFINITION) !== 0) {
    match.isDefinition = true;
  }

  return match;
}

/**
 * Parse an FffGrepResult from a raw FffResult pointer, then free native memory.
 */
function parseGrepResult(resultPtr: Pointer | null): Result<GrepResult> {
  const envelope = readResultEnvelope(resultPtr);
  if (!("success" in envelope)) return envelope;

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

  const hp = asPtr(envelope.handlePtr);
  const count = read.u32(hp, GR_COUNT);
  const totalMatched = read.u32(hp, GR_MATCHED);
  const totalFilesSearched = read.u32(hp, GR_FILES_SEARCHED);
  const totalFiles = read.u32(hp, GR_TOTAL_FILES);
  const filteredFileCount = read.u32(hp, GR_FILTERED);
  const nextFileOffset = read.u32(hp, GR_NEXT_OFFSET);
  const regexErrPtr = read.ptr(hp, GR_REGEX_ERR);
  const regexFallbackError = readCString(regexErrPtr) ?? undefined;
  const itemsBase = read.ptr(hp, GR_ITEMS);

  const items: GrepMatch[] = [];
  for (let i = 0; i < count; i++) {
    items.push(readGrepMatchStruct(itemsBase + i * GM_SIZE_OF));
  }

View on GitHub (pinned to 7f8537e70f)