Explain why -faces embedded no regions (issue #30) - #33
Merged
Conversation
A -faces run that wrote no face region said nothing at all: five separate guards returned silently, so "no Face regions line" was indistinguishable between "no named people", "rotation cannot be anchored", "file has no dimensions", "face boxes carry no named person", and an unwritable video container. Nurgak's report is exactly this ambiguity — his video shows date changes and no face line, and the output cannot say which guard hit. appendFaceRegionChange now returns a reason for each of those, surfaced as a "Face regions (skipped) -> <reason>" entry in the diff block (the one place a piped or -y run still prints) and appended to the skip message when there was nothing else to embed. The faces fetch deliberately stays ahead of the dimension guard so a missing face.read permission is still reported loudly.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves observability for -faces runs by explaining why face-region embedding is skipped, so “no Face regions line” output can be diagnosed (especially for videos) without guessing which guard condition fired.
Changes:
- Extend
appendFaceRegionChangeto return a human-readable skip reason and surface it in both skip messages and diff output. - Emit a
(skipped)“Face regions” diff entry when other metadata changes exist but face regions were not written. - Add tests asserting each skip guard produces an explanatory reason and that non-
-facesruns remain silent.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/process/pipeline.go | Plumbs faceSkip through processing, appending it to skip messages and diff output so -faces skips aren’t silent. |
| src/process/faces.go | Updates face-region embedding logic to return explicit skip reasons for each guard path. |
| src/process/faceSkipReason_test.go | Adds coverage to ensure every skip guard is explained and that successful embeds report no reason. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
HasFaceRegionsToEmbed returns false for a video for two different reasons — an unwritable container, or nobody named on the asset — and the skip reason branched on IsVideoAsset, so a perfectly supported .mp4/.MOV with no named people was told "this video container cannot hold face regions". That is the exact case this diagnostic PR is meant to clarify, and it would send the reporter chasing a container problem that does not exist. Branch on IsUnsupportedVideoAsset instead, and cover the supported-video-no-people case.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
src/process/faces.go:42
- The skip reason for un-anchorable video rotation is grammatically incomplete ("only 0°, 90° and 270° are") and reads awkwardly in both the diff output and skipped message. Consider rephrasing to a complete sentence so users can understand it quickly.
if !ok {
return changes, nil, fmt.Sprintf("video rotation %d° cannot be anchored (only 0°, 90° and 270° are)", intTag(existing, "Rotation")), nil
}
src/process/faces.go:59
- When GetAssetFaces returns 0 faces, the current reason string ends up as "none of Immich's 0 face box(es)…", which is confusing. It would be clearer to special-case 0 faces and report that Immich returned no face boxes for the asset.
regions := exif.BuildFaceRegions(faces, orientation)
if len(regions) == 0 {
return changes, nil, fmt.Sprintf("none of Immich's %d face box(es) carry a named person with dimensions", len(faces)), nil
}
src/process/faceSkipReason_test.go:120
- This test name says "RegionsMatch", but the setup has no existing XMP-mwg-rs:RegionInfo in
tags, and the assertions expect a change to be generated (len(changes)==1). Renaming the test to reflect that it verifies "no skip reason when regions are written" would make the intent clearer.
func TestAppendFaceRegionChangeNoReasonWhenRegionsMatch(t *testing.T) {
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #30. @Nurgak reports on v1.0.5 that a video shows date changes but no
Face regionsline, while an image works.The actual problem
I reproduced that exact output — dates listed, no face line,
1 succeeded— from five different causes. Every guard inappendFaceRegionChangereturned silently, so the output cannot distinguish:So the report can't be diagnosed from the logs — by anyone, including the reporter. That ambiguity is the bug this PR fixes; it does not by itself change which assets get regions.
What changed
appendFaceRegionChangenow returns a reason for each guard. It's surfaced two ways:-yrun still prints:One ordering detail preserved deliberately: the
GET /facescall stays ahead of the dimension guard so a missingface.readpermission is still reported loudly rather than short-circuited. The existing permission tests caught this when I first had it the other way round.Validation
Verified against a live Immich v3.0.1 — an image with no named people now renders:
go vet,gofmt, full suite green. New tests assert each of the five guards names itself, that a run without-facesstays silent, and that a successful embed reports no reason.Next step for the report
With this, one
-dry-runon the affected video tells us which guard fires, instead of guessing between five.