Skip to content

fix: don't write empty tag when registry returns no tags#1505

Merged
chengfang merged 3 commits intoargoproj-labs:masterfrom
mark-liu:fix/empty-registry-tags
Mar 9, 2026
Merged

fix: don't write empty tag when registry returns no tags#1505
chengfang merged 3 commits intoargoproj-labs:masterfrom
mark-liu:fix/empty-registry-tags

Conversation

@mark-liu
Copy link
Contributor

@mark-liu mark-liu commented Feb 19, 2026

Fixes #1242

When a registry returns zero tags (happened during Bitnami's SemVer → Digest migration), GetNewestVersionFromTags returned the current ImageTag which could be a dummy with empty TagName. This bypassed the nil guard in UpdateApplication and wrote image.tag: "" to the manifest.

Fix is one line — return nil instead of img.ImageTag when no tags are available. The existing skip logic in UpdateApplication already handles nil correctly.

  • Also changes behavior for semver strategy when ALL tags are non-semver (e.g. latest, alpine) — these were silently treated as "up-to-date", now correctly classified as "skipped" with a warning log. No update happened either way, but users may see new WARN no tags found messages for misconfigured images.

Related

This PR fixes the "registry returns no tags" path into empty tag write-back. A separate code path exists in marshalParamsOverride (update.go:~438) where GetTagWithDigest() returns "" when a pod scales to zero — reported in #1523 by @Resousse. That requires a separate guard and should be addressed in its own PR.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling when image registries return no tags or no suitable versions are found.
    • Updated skip behavior to correctly identify and handle edge cases with empty tag responses.
    • Enhanced diagnostic logging when registries return no tags or when version constraints have no matching tags.

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5074c6c4-cba6-4a0b-b051-0725f3c23759

📥 Commits

Reviewing files that changed from the base of the PR and between c41f9d7 and 83c6603.

📒 Files selected for processing (3)
  • pkg/argocd/update_test.go
  • registry-scanner/pkg/image/version.go
  • registry-scanner/pkg/image/version_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/argocd/update_test.go

Walkthrough

GetNewestVersionFromTags now returns nil (and logs a warning) when a registry returns no tags or when no suitable version matches; tests in image versioning and application update were adjusted/added to assert images are skipped in those cases.

Changes

Cohort / File(s) Summary
Image version handling
registry-scanner/pkg/image/version.go, registry-scanner/pkg/image/version_test.go
GetNewestVersionFromTags now short-circuits and returns (nil, nil) (with a warning) when availableTags is empty or when no tag satisfies the constraint; tests updated to expect nil for empty/invalid tag sets.
Application update tests
pkg/argocd/update_test.go
Updated existing UpdateApplication expectations to treat non-semver or empty tag results as skipped (NumSkipped == 1); added a test "Test skip when registry returns empty tags (issue #1242)" validating skip behavior and related counters.

Sequence Diagram(s)

sequenceDiagram
    participant Updater as Image Updater
    participant Scanner as Registry Scanner
    participant Version as GetNewestVersionFromTags
    participant App as Application Processor

    Updater->>Scanner: Request tags for image
    Scanner-->>Version: Provide tag list (may be empty)
    Version->>Version: evaluate tags against constraint
    alt tags empty or no matching versions
        Version-->>Scanner: return nil (and log warning)
        Scanner-->>Updater: no newest version (nil)
        Updater->>App: skip image update (increment NumSkipped)
    else valid newest version found
        Version-->>Scanner: return newest version
        Scanner-->>Updater: newest version
        Updater->>App: apply update (increment NumImagesUpdated)
    end
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: preventing empty tags from being written when a registry returns no tags, which directly addresses issue #1242.
Linked Issues check ✅ Passed The PR successfully implements the requirements from issue #1242: returns nil instead of empty tags when registry has no tags, logs warnings appropriately, and skips updating when no tags are available.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #1242 by modifying GetNewestVersionFromTags behavior and updating corresponding tests; the separate scale-down issue is acknowledged and deferred to PR #1531.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link

codecov-commenter commented Feb 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.51%. Comparing base (c1674be) to head (83c6603).
⚠️ Report is 57 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1505      +/-   ##
==========================================
+ Coverage   71.48%   73.51%   +2.03%     
==========================================
  Files          50       53       +3     
  Lines        4667     5098     +431     
==========================================
+ Hits         3336     3748     +412     
- Misses       1133     1143      +10     
- Partials      198      207       +9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
registry-scanner/pkg/image/version.go (1)

81-83: Stale function comment — update to reflect nil-return semantics

The docstring says "Returns the original version if no new version could be found from the list of tags." After this PR, the function now returns nil in that situation, not the original image tag.

✏️ Suggested docstring update
 // GetNewestVersionFromTags returns the latest available version from a list of
 // tags while optionally taking a semver constraint into account. Returns the
-// original version if no new version could be found from the list of tags.
+// nil version if no new version could be found from the list of tags, or if
+// the registry returned no tags at all.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@registry-scanner/pkg/image/version.go` around lines 81 - 83, The docstring
for GetNewestVersionFromTags is stale: it currently says "Returns the original
version if no new version could be found," but the function now returns nil in
that case; update the comment for GetNewestVersionFromTags to accurately state
that it returns a pointer to the newest version matching the (optional) semver
constraint or nil when no newer version is found (and preserve any existing
mention of error or constraint behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@registry-scanner/pkg/image/version.go`:
- Around line 81-83: The docstring for GetNewestVersionFromTags is stale: it
currently says "Returns the original version if no new version could be found,"
but the function now returns nil in that case; update the comment for
GetNewestVersionFromTags to accurately state that it returns a pointer to the
newest version matching the (optional) semver constraint or nil when no newer
version is found (and preserve any existing mention of error or constraint
behavior).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
registry-scanner/pkg/image/version.go (1)

149-157: Silent nil return when tags exist but none pass constraint/strategy filtering.

Line 149 logs at Debug level ("found 0 from N tags eligible for consideration"), but there is no Warn-level message when considerTags is empty. This creates an observability gap: users whose constraint or ignore-list filters out every tag get no actionable signal at the default log level, while the empty-registry path (line 103) now does warn.

Consider adding a warning here for parity:

💡 Proposed addition
 	if len(considerTags) > 0 {
 		return considerTags[len(considerTags)-1], nil
 	}

+	logCtx.Warnf("no tags for image %s matched the current constraint/strategy (checked %d tags)", img.GetFullNameWithoutTag(), len(availableTags))
 	return nil, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@registry-scanner/pkg/image/version.go` around lines 149 - 157, The function
in registry-scanner/pkg/image/version.go currently logs at Debug when no tags
pass constraint/strategy filtering (logCtx.Debugf with considerTags length), but
then silently returns nil; add a Warn-level log (using logCtx.Warnf or
logCtx.Warn) in the branch where len(considerTags) == 0 AND len(availableTags) >
0 to surface that all candidate tags were filtered out by constraints/ignore
rules; reference the existing logCtx usage and the return path that currently
returns nil so the warning is emitted before the final return to provide parity
with the empty-registry warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@registry-scanner/pkg/image/version.go`:
- Around line 102-105: The warning misattributes an empty availableTags to the
registry when tags may have been filtered out by SortBySemVer; modify the logic
around SortBySemVer/availableTags so you check the pre-filter tag count (e.g.,
the original tags slice returned from the registry) and update the log via
logCtx.Warnf to distinguish "no tags in registry" vs "registry returned X tags
but none matched semver (filtered by SortBySemVer) for image %s" using
img.GetFullNameWithoutTag() to provide context.

---

Nitpick comments:
In `@registry-scanner/pkg/image/version.go`:
- Around line 149-157: The function in registry-scanner/pkg/image/version.go
currently logs at Debug when no tags pass constraint/strategy filtering
(logCtx.Debugf with considerTags length), but then silently returns nil; add a
Warn-level log (using logCtx.Warnf or logCtx.Warn) in the branch where
len(considerTags) == 0 AND len(availableTags) > 0 to surface that all candidate
tags were filtered out by constraints/ignore rules; reference the existing
logCtx usage and the return path that currently returns nil so the warning is
emitted before the final return to provide parity with the empty-registry
warning.

@dkarpele dkarpele changed the title Don't write empty tag when registry returns no tags fix: don't write empty tag when registry returns no tags Feb 19, 2026
@mark-liu
Copy link
Contributor Author

Friendly ping — this fixes a data loss edge case where an empty tag gets written. Would appreciate a review when you get a chance.

@Resousse
Copy link
Contributor

Resousse commented Mar 1, 2026

Hello @mark-liu , I've reported a similar bug, but more linked to a pod scale-down (rather than a registry giving no tag), resulting an empty image tag, do you know by any chance if your implementation will solve this too? Thanks

@mark-liu
Copy link
Contributor Author

mark-liu commented Mar 1, 2026

Hey @Resousse, thanks for linking #1523.

I suspect so.

When the pod scales to zero, ArgoCD's Status.Summary.Images either goes empty or retains the stale image ref. In both cases the path through GetNewestVersionFromTags is what determines whether a write-back happens:

  • Images empty, ForceUpdate=false (the default): image isn't found in the live list, skipped at the ContainsImage check. No write-back.
  • Images empty, ForceUpdate=true: a dummy ImageTag with empty TagName gets created, but GetNewestVersionFromTags still runs against the registry. Pre-fix, if no tags matched, it returned that dummy empty tag and the write-back proceeded with "". Post-fix, it returns nil and the image is skipped.
  • Images still has stale ref: proceeds normally with the real tag — no issue.

There's also a defence-in-depth guard in SetHelmImage that skips setting the tag parameter when GetTagWithDigest() is empty, so even if something slips through, the empty value shouldn't reach your values file.

Not sure if you can, but could you try this branch against your setup and confirm it stops the loop?

@Resousse
Copy link
Contributor

Resousse commented Mar 1, 2026

@mark-liu I've tested your branch, and unfortunately, it doesn't solve my issue. As soon as, the scale down is done, an empty tag "" is write back in git causing the loop again :/

@Resousse
Copy link
Contributor

Resousse commented Mar 1, 2026

Ok, I found the issue, in file update.go, around line 440:
err = setHelmValue(&helmNewValues, helmParamVersion, tagValue)
tagValue when retrieved from c.ContainerImage.GetTagWithDigest can be an empty string, if the container is not running anymore (my scale down case)
To avoid writing an empty string, I've just added a condition on tagValue to not update the tag, when the target tag is empty.

if tagValue != "" {
		err = setHelmValue(&helmNewValues, helmParamVersion, tagValue)
		if err != nil {
			return nil, fmt.Errorf("failed to set image parameter version value: %v", err)
		}
	}

I've tested this based on your branch, and it works as expected

@mark-liu do you mind adding this code to your PR, as it's related to empty tag ?
Thank you very much

@mark-liu
Copy link
Contributor Author

mark-liu commented Mar 1, 2026

Thanks for the investigation @Resousse. You've found a separate code path — this PR guards the "registry returns no tags" entry point (version.go), but your scale-down case enters through marshalParamsOverride (update.go:~438) where GetTagWithDigest() returns "" on an un-aliased config image.

Your proposed guard looks good to me:

if tagValue != "" {
    err = setHelmValue(&helmNewValues, helmParamVersion, tagValue)
}

When the file already exists, helmNewValues is populated from the original data (line 403-404), so skipping the overwrite preserves the previous good tag. When the file doesn't exist yet, omitting an empty tag is the right behavior.

Worth noting: the ArgoCD write-back path already has this guard at argocd.go:766 — only the git write-back custom target path is missing it.

Since this is a different trigger and code path from #1242, it should be a separate PR linked to #1523. Would you like to open one? The fix is a one-line guard at update.go:438.

@Resousse
Copy link
Contributor

Resousse commented Mar 2, 2026

Sure, just created this : #1531

mark-liu added 3 commits March 6, 2026 16:00
Signed-off-by: Mark Liu <mark@prove.com.au>
Signed-off-by: Mark Liu <mark@prove.com.au>
Distinguish between registry returning no tags vs strategy/constraint
filtering all tags out, so operators can diagnose the root cause.

Signed-off-by: Mark Liu <mark@prove.com.au>
@chengfang chengfang force-pushed the fix/empty-registry-tags branch from 554284b to 83c6603 Compare March 6, 2026 21:00
@chengfang chengfang merged commit c002337 into argoproj-labs:master Mar 9, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Image updater removes tags when registry is empty.

4 participants