Your detection script stops on its second line: OSError: [Errno 2] ENOENT, raised by image.HaarCascade("frontalface", stages=25). Your parameters are not the problem, and neither is the camera. Errno 2 is the operating system reporting that the cascade file is not there: the loader resolved the argument to a concrete location on the embedded camera and found nothing to open. This guide decodes the error, walks the three causes in the order they occur on real hardware, gives the five-minute fix for each, and shows how to make model paths deterministic so the failure does not come back.
What errno 2 actually tells you
ENOENT is errno 2 in the standard C library: "no such file or directory". The loader ran, resolved what you passed into a concrete path, asked the operating system to open it, and the OS found nothing at that location. Everything downstream — the 25 cascade stages, the image pipeline, the detection itself — never executes. Treat it as a map problem, not a model problem.
The neighbouring errnos fail for different reasons and need different fixes:
| Errno | Name | The OS means | Where the real problem sits |
|---|---|---|---|
| 2 | ENOENT | Nothing exists at this path | File absent, name misspelled, or a relative path in the wrong directory |
| 13 | EACCES | The file exists but may not be opened | Permissions or a read-only mount, not location |
| 19 | ENODEV | The device itself is gone | Disconnect or unmounted storage; see the errno 19 disconnect fix |
| 30 | EROFS | You tried to write a read-only filesystem | Saving into the ROM filesystem instead of writable storage |
Because errno 2 fires at file open, the stages=25 argument — a request for a 25-stage cascade — plays no part in it. Fix the location first; tune the stage count afterwards.
Cause 1: the stock cascade is missing from the ROM filesystem
On MicroPython-class vision boards the stock cascades are not loose files on an SD card. They live in a read-only ROM filesystem (ROMFS) image flashed together with the firmware, and a bare name such as frontalface is looked up inside that image and nowhere else.
Two things empty it: a firmware image older than the shipped examples, and a ROMFS image flashed without the default files. On this board family, the update that shipped the corrected examples also reset the ROM filesystem during the IDE upgrade — v4.7.0 did that step automatically — so a board flashed from an old image is the usual patient when code that ran a year ago now fails.
The fix takes two steps:
- Update the IDE and flash the current firmware for the board.
- Reset the ROM filesystem from the IDE menu: Tools → ROMFS → Reset ROMFS.
- Re-run the exact failing line, unchanged.
If errno 2 still appears after a clean reset, the name you passed is not a stock name for your firmware build, and you are in cause 2.
Cause 2: the cascade is on your PC and never reached the device
A cascade you trained exists only on the workstation. The camera cannot see the PC's disk, so any path copied out of a training script fails on the device — the file was never there.
Desktop OpenCV hides this: its data files install inside the package and cv2.data.haarcascades points at that folder, so haarcascade_frontalface_default.xml always resolves. An embedded camera has no such folder until you create one and fill it.
Move the file across with the IDE's file-transfer tools, an SD card, or on a controller-class device a plain network copy into a models directory. Verify the copy before you load it:
import os
cascade_path = "/models/frontalface25.cascade" # illustrative device-side path (str)
print(os.listdir("/models")) # standard Python: what actually landed on the device
face_cascade = image.HaarCascade(cascade_path, stages=25)
The listing is the test: if the cascade's file name is not in the printed list, the transfer failed, and the loader will raise the same errno 2 — correctly.
Cause 3: the file is there, the path is not
Three spelling-level failures produce the same errno on a healthy copy:
-
Case. On-board filesystems are case-sensitive:
frontalfaceandFrontalFaceresolve differently. - Relative paths. A script run from the IDE inherits one working directory; the same script launched at boot usually starts in another, so a relative path opens nothing in one of the two runs.
- Foreign separators. A path pasted from a Windows training machine carries backslashes or a drive letter the device cannot parse.
Deployments that pass in the IDE and fail on the line are almost always the middle case; the model file path deployment fix goes deeper, and the cross-OS path error guide covers the separator side. Print before you load:
import os
print(os.getcwd()) # where relative paths resolve right now
os.stat("/models/frontalface25.cascade") # raises errno 2 here, before the loader
If the path was assembled by string concatenation, the escaping traps are the same ones that bite GenTL CTI file paths.
Worked example: error to detection in five minutes
Take the failing call and walk it end to end:
- Re-run it unchanged and read the message.
OSError: [Errno 2] ENOENTconfirms a missing file; a corrupt file would get past the open and fail later, at parse time. - Look at what you passed. A bare name (
frontalface) means a stock ROMFS lookup; anything containing a separator is a path into device storage. - Bare name: flash the current firmware, run Tools → ROMFS → Reset ROMFS, re-run. This clears the most common case outright.
- Path: run the two-line check from cause 3. An errno 2 from the
os.statcall pins the failure to the exact path before any model code runs. - Copy the file, list the directory, confirm the name in the output, then load again.
- Only now weigh
stages=25: the stage count trades detection speed against recall, and it is read after the file opens.
Capacity is almost never the suspect. On a vision controller such as the MV-VC2140-128G40-1T, with 8 GB memory, a 128 GB SSD and a 1 TB HDD, a whole cascade library amounts to a few megabytes; on an on-device smart camera the constraint is where the file lives, not how much room it takes.
Prevention: make every model path deterministic
- Keep one models directory on the device, referenced by absolute path from every script, IDE run or boot run alike.
- Check existence before the constructor:
os.statthe exact path and raise your own error naming it, so a future failure says which file vanished. - Copy, then verify: after every transfer, list the directory and confirm the file before the line that loads it.
- After any firmware update, re-run Tools → ROMFS → Reset ROMFS so stock names stay present, and re-check any custom files you keep on the board.
- Log the resolved path inside the exception handler instead of the bare errno.
- Keep the deployed model folder under version control, and pin library versions the way SDK version pinning prevents edge inference crashes.
Once the file opens, the classifier itself still has limits of its own; the cascade rotation and scale guide shows what a cascade matched to flat parts will and will not tolerate.
When it is not a path problem at all
- Every open fails, not just the cascade: storage is absent or unmounted, and the fix is the mount, not the path.
- The script never reaches the loader and the camera drops its first frames after power-up: that is the first-images ready-gate fault, a different failure with its own fix.
- Saving results fails with errno 30: you are writing into the read-only ROM filesystem. Captured data belongs in writable storage, as storing images on vision board flash shows for C deployments.
Parts for detection that runs on the device
If the plan is detection on the camera itself, with no PC in the loop, the smart cameras category is built for inspection on the device:
- Interface
- USB 2.0
- Resolution
- 6-10 MP
- Sensor format
- 1"
- Shutter
- Global
Where several cameras share one model folder and one home, vision controllers give every script a conventional filesystem on their own SSD:
- LAN ports
- 3-4 LAN ports
- USB 3 ports
- 2 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
Either way the model and the loader end up on the same filesystem, and the path you print is the path that opens. To size the camera side, filter the camera finder by interface, resolution and lens mount; the systems topic page collects the rest of the on-device debugging guides, and the contact page is the route to a quoted set-up.
Questions
What does errno 2, ENOENT, actually mean?
The operating system returned its error number 2, ENOENT, because a file open found nothing at the path the loader resolved. It is a location error, not corruption or a syntax problem: the detection code never ran. Fix where the file is and how the path is spelled, not the algorithm or the stage count.
Where do the built-in cascades live on the camera?
In a read-only ROM filesystem image flashed alongside the firmware, not on removable storage. The loader resolves a bare name such as "frontalface" inside that image. If your firmware predates the cascade, or the image was replaced without the default files, the name resolves to nothing and the open fails with errno 2.
Can I train my own cascade and run it on the board?
Yes. Train on a workstation, copy the finished cascade into device storage through the IDE's file tools, an SD card or a network share, then pass the device-side absolute path to the loader instead of a built-in name. List the destination directory first so you confirm the file landed where your path claims before detection runs.
Why does the same script work in the IDE but fail after deployment?
Relative paths resolve against the current working directory, and a script launched at boot starts in a different directory than the same script run from the IDE. The cascade exists, but the relative path resolves elsewhere and opens nothing. Use absolute device-side paths so both runs resolve the identical file.
How is ENOENT different from a permissions error?
Errno 13, EACCES, means the file exists but the process may not open it; errno 2, ENOENT, means nothing exists at that path. Check existence first with a directory listing or a stat at the exact path, then permissions. If listing the directory itself fails with errno 2, the whole directory is missing or the path is misspelled.

