TL;DR SearchFacesByImage first detects the largest face in your input image. If it finds none, it fails with
InvalidParameterException: There are no faces in the image. Should be at least 1.The usual causes are a face that is small relative to the frame, a selfie cropped too tightly, an extreme head pose, or a dark or blurry photo. CallDetectFaceson the same bytes to see which one you hit, crop group shots around the face with a margin, and catch the exception so your user gets "try another photo" instead of a 500.
This is the error every selfie-search feature runs into in its first week of real traffic. Test photos work. Then real users upload a photo of themselves from across the room at a wedding, or a selfie their camera app already cropped to the face, and the call fails before any searching happens.
botocore.errorfactory.InvalidParameterException: An error occurred (InvalidParameterException)
when calling the SearchFacesByImage operation: There are no faces in the image. Should be at least 1.
InvalidParameterException is a generic "input violated a constraint" error with HTTP status 400, so the message text is the only thing that tells you it was face detection that failed and not, say, a bad CollectionId.
What SearchFacesByImage actually does with your image
Per the API reference, the operation runs detection first, takes the single largest face it found, and only then searches the collection with it. Two consequences matter:
-
It only ever searches one face. In a group photo, it picks the largest face, which may not be the person who uploaded it. To search every face in a photo, AWS suggests
IndexFacesand thenSearchFacesby face ID. -
Detection failure is an exception, not an empty result. A photo where Rekognition sees no face never reaches the search step, so you can't get back an empty
FaceMatcheslist for it.
The six causes, most common first
1. The face is small relative to the image
The quota page says a face must be at least 40×40 pixels in a 1920×1080 image, and that larger images need a proportionally larger face. The input-image recommendations give 50×50 at the same resolution. Either way, the limit is relative. A face that is 60 pixels tall in a 6000×4000 photo behaves like a face of about 16 pixels in a 1080p frame, far below the minimum.
This is why downscaling a large photo doesn't help: the face shrinks with it. Cropping does help, because it makes the face a larger share of the frame.
2. The selfie is cropped too tightly
The opposite mistake. Some upload widgets and camera apps crop to the face before you ever see the image. AWS's recommendation is explicit: the image "should contain the full head and shoulders of the person. It should not be cropped to the face bounding box." If your client code crops, leave a margin of roughly one face-width on every side.
3. Extreme head pose
AWS recommends pitch under 30° looking down and under 45° looking up, and yaw under 45° either way. Roll (head tilt) has no restriction, so a sideways phone photo is rarely the cause on its own. Profile shots and people looking down at a phone are.
4. Dark, blurry or low-contrast photos
Night-time event photos with motion blur, heavy backlight, or a face in deep shadow. IndexFaces reports these as UnindexedFaces with reasons like LOW_BRIGHTNESS and LOW_SHARPNESS, and SearchFacesByImage simply fails to find a face.
5. Occlusion
Sunglasses, masks, a hand over the mouth, or a phone in front of the face during a mirror selfie. AWS asks for both eyes open and visible.
6. A QualityFilter you set yourself
SearchFacesByImage defaults QualityFilter to NONE. If your code sets LOW, MEDIUM or HIGH, a borderline selfie can be filtered out before search. Set it back to NONE while debugging to see whether the filter or detection is what rejects the face.
Info: Not the same error:
InvalidImageFormatExceptionmeans the bytes aren't a PNG or JPEG. Rekognition accepts only those two formats, so iPhone HEIC uploads fail with it, and so does passing a base64 string toImage={"Bytes": ...}in boto3 (boto3 encodes the bytes for you).ImageTooLargeExceptionmeans raw bytes over 5 MB; S3 objects can be up to 15 MB.
Diagnose with one DetectFaces call
DetectFaces with Attributes=["ALL"] returns every face it sees with its bounding box, pose and quality scores. Run it on the exact bytes that failed. If it returns nothing, the problem is the image. If it returns a face, the numbers tell you which cause above you hit. Note that DetectFaces is a separately billed call, so use it for debugging and sampling, not on every request.
Run on the bytes that failed. Prints size relative to the 1080p minimum, pose and quality.
import boto3
from PIL import Image
import io
rek = boto3.client("rekognition", region_name="us-east-1")
def explain(image_bytes: bytes) -> None:
w, h = Image.open(io.BytesIO(image_bytes)).size
faces = rek.detect_faces(Image={"Bytes": image_bytes}, Attributes=["ALL"])["FaceDetails"]
if not faces:
print(f"{w}x{h}: no face detected at all")
return
for f in faces:
box = f["BoundingBox"]
face_px = min(box["Width"] * w, box["Height"] * h)
# Scale to what this face would measure in a 1920x1080 frame.
at_1080p = face_px * 1080 / min(w, h)
p, q = f["Pose"], f["Quality"]
print(
f"face ~{face_px:.0f}px ({at_1080p:.0f}px at 1080p, min ~40-50) "
f"yaw={p['Yaw']:.0f} pitch={p['Pitch']:.0f} "
f"brightness={q['Brightness']:.0f} sharpness={q['Sharpness']:.0f}"
)
Handle it in code instead of returning a 500
The error is a user-input problem, so treat it like a validation error. Catch it by code and message, and ask for another photo.
from botocore.exceptions import ClientError
def search_selfie(collection_id: str, image_bytes: bytes):
try:
return rek.search_faces_by_image(
CollectionId=collection_id,
Image={"Bytes": image_bytes},
FaceMatchThreshold=90,
MaxFaces=50,
)["FaceMatches"]
except ClientError as e:
err = e.response["Error"]
if err["Code"] == "InvalidParameterException" and "no faces" in err["Message"].lower():
return None # tell the user: "We couldn't see a face. Try a closer, well-lit photo."
raise
Prepare images so the error happens less
-
Apply EXIF rotation and convert to JPEG. Phones store orientation as metadata and often shoot HEIC.
ImageOps.exif_transposeplus a JPEG re-encode fixes both, and keeps you under the 5 MB bytes limit. - Crop group shots around the chosen face, with a margin. If your UI lets the user tap their face in a group photo, crop to that region plus about one face-width on each side. The face becomes a larger share of the frame without violating the "head and shoulders" advice.
- Validate at upload, not at search. Run detection once when the user uploads the selfie and reject it right away with a clear message. Users accept "retake" at upload time far better than an empty result later.
Rotation, optional crop with margin, JPEG under the 5 MB bytes limit.
import io
from PIL import Image, ImageOps
def prepare(path: str, crop=None, max_side=2048) -> bytes:
img = ImageOps.exif_transpose(Image.open(path)).convert("RGB")
if crop: # (left, top, width, height) as ratios, e.g. from a tap or a detector
l, t, cw, ch = crop
W, H = img.size
mx, my = cw * W, ch * H # one face-width of margin each side
img = img.crop((
max(0, l * W - mx), max(0, t * H - my),
min(W, (l + cw) * W + mx), min(H, (t + ch) * H + my),
))
img.thumbnail((max_side, max_side))
buf = io.BytesIO()
img.save(buf, "JPEG", quality=90)
return buf.getvalue()
The same flow on SightRadar
We build SightRadar, a face search API that keeps Rekognition's collection model (create a collection, index photos, search by selfie), so this is a problem we handle every day. Two differences are relevant here:
-
No exception for a missing face. Search returns
200with an emptymatcheslist and areasonofno_faceorlow_quality_selfie, so the "ask for another photo" branch is a field check, not an exception handler. Like any processed photo, the call is billed. - More input formats. JPEG, PNG, WebP, HEIC/HEIF, TIFF, GIF and BMP are decoded natively and the limit is 30 MB, so the format and size errors above don't apply. The relative-size and pose advice still does: it's how face detection works, not a vendor quirk.
import os, requests
r = requests.post(
f"https://api.sightradar.com/v1/collections/{collection_id}/search",
headers={"Authorization": f"Bearer {os.environ['SIGHTRADAR_API_KEY']}"},
files={"file": open("selfie.jpg", "rb")},
data={"limit": "50"},
timeout=30,
)
body = r.json()
if not body["matches"] and body.get("reason") in ("no_face", "low_quality_selfie"):
print("Ask for another photo:", body["reason"])
for m in body["matches"]:
print(m["photo_id"], round(m["score"], 3))
If you already have boto3 code, the Rekognition shim keeps your search_faces_by_image calls and converts the 0 to 100 threshold scale. The migration guide covers re-indexing and cut-over.
Try detection on your own problem photos. New accounts get free credits, no card. Open the face detector
Frequently asked questions
Why does SearchFacesByImage say there are no faces when I can clearly see one?
Rekognition's minimum face size is relative to the image: about 40 to 50 pixels in a 1920x1080 frame, scaled up for larger images. A face that looks fine to you in a 24-megapixel group photo can be far below that. Tight crops, extreme head pose, darkness, blur and occlusion are the other common causes.
Does resizing the image fix the no faces error?
Downscaling does not, because the face shrinks in proportion. Cropping around the face does, because the face becomes a larger share of the frame. Keep a margin so the crop still shows head and shoulders.
How do I search every face in a group photo with Rekognition?
SearchFacesByImage only searches the largest face. To search every face, call IndexFaces on the photo, then call SearchFaces with each returned FaceId.
What is the difference between InvalidParameterException and InvalidImageFormatException here?
InvalidParameterException with the message 'There are no faces in the image' means the image decoded but no face was detected. InvalidImageFormatException means the bytes are not a PNG or JPEG, for example an iPhone HEIC file or a base64 string passed where raw bytes are expected.
I work on SightRadar, the face search API used in the examples. Originally published on the SightRadar blog.
This article was written with AI.
Top comments (0)