microsoft/typescript-go · error
CFStringCreate returned NULL
Error message
CFStringCreate returned NULL
What it means
On macOS, building an FSEvents stream first converts each watched path to a CFString; if CoreFoundation's CFStringCreate returns NULL (allocation failure), this sentinel error is returned and the stream is never created. It is effectively an out-of-memory condition at the CF layer.
Source
Thrown at internal/fswatch/fsevents_darwin.go:213
func (b *fsEventsBackend) start() error {
b.notifyStarted()
return nil
}
// checkWatcher mirrors the helper of the same name.
func checkWatcher(w *dirWatch) error {
info, err := os.Stat(w.physicalDir)
if err != nil {
return &dirWatchError{err: err, dirWatch: w}
}
if !info.IsDir() {
return &dirWatchError{err: syscall.ENOTDIR, dirWatch: w}
}
return nil
}
var (
errCFStringCreateNull = errors.New("CFStringCreate returned NULL")
errCFArrayCreateNull = errors.New("CFArrayCreate returned NULL")
errStreamCreateNull = errors.New("FSEventStreamCreate returned NULL")
errStreamStartFailed = errors.New("error starting FSEvents stream")
)
var (
errFSEventsUserDropped = fmt.Errorf("events were dropped by the FSEvents client: %w", ErrOverflow)
errFSEventsKernelDropped = fmt.Errorf("events were dropped by the kernel: %w", ErrOverflow)
errFSEventsTooMany = fmt.Errorf("too many events: %w", ErrOverflow)
)
const fseventsPathsPerStream = 512
type fseventsWatchSnapshot struct {
w *dirWatch
state *fseventsState
}
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Free memory / raise the container or runner memory limit before creating watches
- Reduce the number and length of watched paths (watch fewer roots, prune ignores) to shrink allocations
- Retry once after a pause — transient CF allocation failures usually clear when memory is released
- Fall back to a polling watcher if FSEvents cannot allocate
Example fix
// before
w, err := fswatch.Default().WatchDirectories(allRequests) // thousands of roots, CFStringCreate NULL
// after: watch few roots recursively instead of listing every directory
w, err := fswatch.Default().WatchDirectory(repoRoot, cb, fswatch.WithRecursive(),
fswatch.WithIgnore(ignoreHeavyDirs)) Defensive patterns
Strategy: retry
Validate before calling
// Check available memory before building large FSEvents subscriptions.
func memHeadroom() bool {
si, err := mem.VirtualMemory() // github.com/pod coconut gopsutil/v3/mem
if err != nil { return true }
return si.Available > 512*1024*1024 // require >= 512MB free
} Type guard
func isCFAllocFailure(err error) bool {
return err != nil && (errors.Is(err, fswatch.ErrUnavailable) == false) &&
(strings.Contains(err.Error(), "CFStringCreate returned NULL") ||
strings.Contains(err.Error(), "CFArrayCreate returned NULL"))
} Try / catch
w, err := watcher.WatchDirectories(reqs)
if err != nil && isCFAllocFailure(err) {
time.Sleep(2 * time.Second) // let memory pressure clear
if w, err = watcher.WatchDirectories(reqs); isCFAllocFailure(err) {
return startPolling(roots(reqs)) // degrade to polling
}
}
if err != nil { return err } Prevention
- Prefer few recursive FSEvents watches over many enumerated paths (natively recursive on macOS)
- Watch RSS in long-lived macOS daemons; restart or trim before CF allocations start failing
- Free/close unused watchers — each holds CF objects that pressure the same allocator
When it happens
Trigger: Creating a watch while the process/system is out of memory; requesting very many paths in one batch so the per-path CFString allocations fail; running inside memory-capped containers/CI executors on macOS runners.
Common situations: Watchers over huge path lists (monorepo recursive watches) on memory-pressured machines; long-running daemons with leaks that finally exhaust the heap; macOS CI hosts near their memory limit.
Related errors
- CFArrayCreate returned NULL
- FSEventStreamCreate returned NULL
- unable to open pipe: %w
- unable to initialize fanotify: %w
- unable to poll: %w
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/e75fdecd0ffb08d0.
Report an issue: GitHub.