Describe the bug
When the remote metadata fetch fails for any reason other than an HTTP 404 on an internal repository, apm pack drops description and version from every remote git-subdir entry, prints [*] Built marketplace.json, writes nothing to stderr, and exits 0. A network outage, a proxy that refuses connections, an expired token, or a rate-limited runner all produce a truncated marketplace.json that looks like a successful build. Consumers then reject the affected plugins, because description is required once emitted.
This is the general case behind #1847, which fixed one specific trigger (raw.githubusercontent.com returning 404 for INTERNAL repos) by adding a REST fallback. The reporting gap it exposed is still open: every other failure mode stays silent.
Two code sites produce the silence:
marketplace/builder.py::_prefetch_metadata collects the thread-pool results under except Exception: pass, so a per-package fetch failure leaves no trace at default verbosity. The same method returns early under --offline without recording that remote packages were skipped.
marketplace/output_mappers.py::_apply_field_with_precedence omits the field when neither the entry value nor the fetched value is present, and emits no diagnostic. The surrounding code does emit BuildDiagnostic warnings for smaller problems, such as a marketplace name that is not kebab-case.
The diagnosis exists but is unreachable in practice: Could not fetch remote metadata for <name> is logged at DEBUG. Running apm pack -v shows nothing; only APM_LOG_LEVEL=DEBUG surfaces it.
The second consequence is worse than the first. --check-clean regenerates the index and diffs it against the file on disk, so when the network is down on both sides, a truncated marketplace.json passes the release gate:
marketplace.json on disk |
Network |
apm pack --check-clean --dry-run |
| truncated |
blocked |
exit 0 — gate passes |
| truncated |
available |
exit 4 — drift detected |
A CI job that runs the gate on a runner with a degraded network therefore certifies a broken index as clean.
To Reproduce
-
Create a project with an apm.yml whose marketplace declares one remote git-subdir package. The reference below is a public repository pinned to a full SHA, so no token and no ref resolution are needed:
name: repro
version: 1.0.0
description: Minimal marketplace with one remote git-subdir package
targets:
- claude
marketplace:
owner:
name: repro-owner
outputs:
claude: {}
versioning:
strategy: per_package
packages:
- name: adapt-nifi-flows-to-2-x
source: Netcracker/qubership-nifi
subdir: agent-packages/adapt-nifi-flows-to-2-x
ref: 09c4bf708d787eba45046904ab5b40b9ac597c6b
-
Run apm pack with the network available. The entry gets "description": "Apply NiFi 1.x to 2.x upgrade advisor recommendations to exported flow JSON files." and "version": "1.2.0", both inherited from the upstream package's own apm.yml.
-
In a fresh directory with the same apm.yml, run apm pack with outbound HTTPS pointed at a closed port. Note that --offline is not passed:
env https_proxy=http://127.0.0.1:9 HTTPS_PROXY=http://127.0.0.1:9 ALL_PROXY=http://127.0.0.1:9 apm pack
-
Observe the exit code and the output:
[*] Built marketplace.json [claude] (1 package(s)) -> .../.claude-plugin/marketplace.json
exit=0
stderr: 0 bytes
The written entry has lost both fields:
{
"name": "adapt-nifi-flows-to-2-x",
"source": {
"source": "git-subdir",
"url": "Netcracker/qubership-nifi",
"path": "agent-packages/adapt-nifi-flows-to-2-x",
"ref": "09c4bf708d787eba45046904ab5b40b9ac597c6b",
"sha": "09c4bf708d787eba45046904ab5b40b9ac597c6b",
"tag_pattern": "v{version}"
}
}
-
Confirm the release gate accepts it. Against the truncated file from step 4:
env https_proxy=http://127.0.0.1:9 HTTPS_PROXY=http://127.0.0.1:9 ALL_PROXY=http://127.0.0.1:9 \
apm pack --check-clean --dry-run; echo "exit=$?" # exit=0
apm pack --check-clean --dry-run; echo "exit=$?" # exit=4
-
Confirm the information is DEBUG-only. apm pack -v mentions metadata zero times; APM_LOG_LEVEL=DEBUG apm pack prints DEBUG apm_cli.marketplace.builder Could not fetch remote metadata for adapt-nifi-flows-to-2-x.
Expected behavior
A failed metadata fetch should be visible at default verbosity, and should be able to fail the build.
- Report it. Every swallowed fetch failure, and every remote package skipped because of
--offline, should surface as a warning-level BuildDiagnostic naming the package and the cause. The build summary should not read [*] Built marketplace.json (1 package(s)) when that package lost half its metadata.
- Let CI reject it. A flag along the lines of
--strict-metadata should promote those warnings to a hard failure. Exit codes 0 through 4 are taken by success, build error, schema error, --check-versions, and --check-clean, so this needs a new one.
- Keep
--check-clean honest. The gate currently compares two artifacts that can be wrong in the same way. Failing the run when the regeneration side degraded would close that hole regardless of what the file on disk contains.
Point 2 is what we need most: a marketplace publisher wants one command that either produces a complete index or fails.
Environment (please complete the following information):
- OS: macOS 15 (Darwin 25.5.0, arm64)
- Python Version: 3.12
- APM Version: 0.28.0 (installed from PyPI via
uvx --from apm-cli==0.28.0); also reproduced on the 0.26.0 native binary and observed in production on 0.24.1
- VSCode Version (if relevant): N/A
Logs
Default verbosity, network blocked, no --offline:
[!] No 'license:' field in apm.yml; the SBOM will record NOASSERTION for this package. ...
[*] Built marketplace.json [claude] (1 package(s)) -> .../.claude-plugin/marketplace.json
[i] Marketplace artifacts ready:
Nothing on stderr. The same run under APM_LOG_LEVEL=DEBUG:
DEBUG apm_cli.marketplace.builder Resolved GitHub token for metadata fetch (source=gh-auth-token)
DEBUG apm_cli.marketplace.builder Could not fetch remote metadata for adapt-nifi-flows-to-2-x
File ".../apm_cli/marketplace/builder.py", line 1026, in _fetch_remote_metadata
Additional context
This has hit us twice in production on Netcracker/qubership-ai-packages, a marketplace with 27 packages of which 10 are remote git-subdir entries. Both times a contributor ran apm pack without working network access, committed the result, and all 10 remote packages lost description and version at once. CI ran apm pack --check-versions --check-clean --dry-run on both commits and passed them. The breakage surfaced only when consumers started rejecting the packages, and was repaired by restoring the fields by hand.
Related: #1847 (the 404-on-INTERNAL-repos trigger, fixed) and #1725 (local virtual-subdirectory packages not enriched).
Describe the bug
When the remote metadata fetch fails for any reason other than an HTTP 404 on an internal repository,
apm packdropsdescriptionandversionfrom every remotegit-subdirentry, prints[*] Built marketplace.json, writes nothing to stderr, and exits 0. A network outage, a proxy that refuses connections, an expired token, or a rate-limited runner all produce a truncatedmarketplace.jsonthat looks like a successful build. Consumers then reject the affected plugins, becausedescriptionis required once emitted.This is the general case behind #1847, which fixed one specific trigger (raw.githubusercontent.com returning 404 for INTERNAL repos) by adding a REST fallback. The reporting gap it exposed is still open: every other failure mode stays silent.
Two code sites produce the silence:
marketplace/builder.py::_prefetch_metadatacollects the thread-pool results underexcept Exception: pass, so a per-package fetch failure leaves no trace at default verbosity. The same method returns early under--offlinewithout recording that remote packages were skipped.marketplace/output_mappers.py::_apply_field_with_precedenceomits the field when neither the entry value nor the fetched value is present, and emits no diagnostic. The surrounding code does emitBuildDiagnosticwarnings for smaller problems, such as a marketplace name that is not kebab-case.The diagnosis exists but is unreachable in practice:
Could not fetch remote metadata for <name>is logged atDEBUG. Runningapm pack -vshows nothing; onlyAPM_LOG_LEVEL=DEBUGsurfaces it.The second consequence is worse than the first.
--check-cleanregenerates the index and diffs it against the file on disk, so when the network is down on both sides, a truncatedmarketplace.jsonpasses the release gate:marketplace.jsonon diskapm pack --check-clean --dry-runA CI job that runs the gate on a runner with a degraded network therefore certifies a broken index as clean.
To Reproduce
Create a project with an
apm.ymlwhose marketplace declares one remotegit-subdirpackage. The reference below is a public repository pinned to a full SHA, so no token and no ref resolution are needed:Run
apm packwith the network available. The entry gets"description": "Apply NiFi 1.x to 2.x upgrade advisor recommendations to exported flow JSON files."and"version": "1.2.0", both inherited from the upstream package's ownapm.yml.In a fresh directory with the same
apm.yml, runapm packwith outbound HTTPS pointed at a closed port. Note that--offlineis not passed:Observe the exit code and the output:
The written entry has lost both fields:
{ "name": "adapt-nifi-flows-to-2-x", "source": { "source": "git-subdir", "url": "Netcracker/qubership-nifi", "path": "agent-packages/adapt-nifi-flows-to-2-x", "ref": "09c4bf708d787eba45046904ab5b40b9ac597c6b", "sha": "09c4bf708d787eba45046904ab5b40b9ac597c6b", "tag_pattern": "v{version}" } }Confirm the release gate accepts it. Against the truncated file from step 4:
Confirm the information is DEBUG-only.
apm pack -vmentions metadata zero times;APM_LOG_LEVEL=DEBUG apm packprintsDEBUG apm_cli.marketplace.builder Could not fetch remote metadata for adapt-nifi-flows-to-2-x.Expected behavior
A failed metadata fetch should be visible at default verbosity, and should be able to fail the build.
--offline, should surface as awarning-levelBuildDiagnosticnaming the package and the cause. The build summary should not read[*] Built marketplace.json (1 package(s))when that package lost half its metadata.--strict-metadatashould promote those warnings to a hard failure. Exit codes 0 through 4 are taken by success, build error, schema error,--check-versions, and--check-clean, so this needs a new one.--check-cleanhonest. The gate currently compares two artifacts that can be wrong in the same way. Failing the run when the regeneration side degraded would close that hole regardless of what the file on disk contains.Point 2 is what we need most: a marketplace publisher wants one command that either produces a complete index or fails.
Environment (please complete the following information):
uvx --from apm-cli==0.28.0); also reproduced on the 0.26.0 native binary and observed in production on 0.24.1Logs
Default verbosity, network blocked, no
--offline:Nothing on stderr. The same run under
APM_LOG_LEVEL=DEBUG:Additional context
This has hit us twice in production on Netcracker/qubership-ai-packages, a marketplace with 27 packages of which 10 are remote
git-subdirentries. Both times a contributor ranapm packwithout working network access, committed the result, and all 10 remote packages lostdescriptionandversionat once. CI ranapm pack --check-versions --check-clean --dry-runon both commits and passed them. The breakage surfaced only when consumers started rejecting the packages, and was repaired by restoring the fields by hand.Related: #1847 (the 404-on-INTERNAL-repos trigger, fixed) and #1725 (local virtual-subdirectory packages not enriched).