openclaw - ✅(Solved) Fix Smoke check falls through after extension-entry failure, producing duplicate failure entries per plugin [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#83891Fetched 2026-05-20 03:47:26
View on GitHub
Comments
1
Participants
2
Timeline
7
Reactions
1
Timeline (top)
labeled ×5commented ×1cross-referenced ×1

Root Cause

Recommendation

Add continue; immediately after the failures.push inside the invalid/empty branch (before the } else if block) to mirror the pattern used for the earlier missing-package-json and missing-package-dir checks. The ok branch's failure path already falls through correctly because checking main after a valid extension resolution is additive, not duplicate.

Fix Action

Fix / Workaround

Severity: low / Confidence: high / Category: bug Triage: confirmed-bug Detected against: openclaw v2026.5.18 (latest stable at time of scan, 2026-05-18) Tooling: clawpatch 0.3.0 + acpx/claude-sonnet-4-5 via Brad Mills protocol


Standardized clawpatch finding. Persistent in v2026.5.18 (not resolved by upgrading from v2026.5.12). Finding ID: fnd_sig-feat-cli-command-0e1f16a0ce-_c41ebbc2f5.

PR fix notes

PR #84284: fix(update): skip main-entry check after missing-extension-entry (#83891)

Description (problem / solution / changelog)

Fixes #83891.

runPluginPayloadSmokeCheck (src/cli/update-cli/plugin-payload-validation.ts:104) walks each plugin install record and runs a series of validation stages. The missing-package-dir and invalid-package-json branches continue to the next record after their failure push, but the missing-extension-entry branch (empty or invalid openclaw.extensions) does NOT continue. Execution falls through into the trailing main-entry check. If the same plugin ALSO declares a main that doesn't exist on disk, a second missing-main-entry failure is appended for the same pluginId.

Downstream impact: in runPostCorePluginConvergence (src/cli/update-cli/post-core-plugin-convergence.ts:74-77), every smoke failure becomes a per-plugin warning, so the duplicate entry double-counts in the post-update warnings list and in the smokeFailures array consumers iterate. errored: warnings.length > 0 still fires correctly in the single-failure case, but any caller that uses failure count for severity decisions would over-weight this plugin.

Changes

  • src/cli/update-cli/plugin-payload-validation.ts: add continue; after the failure push in the extensionResolution.status === "invalid" || "empty" branch. The comment above the new statement points at the existing continue; in the earlier missing-package-dir / invalid-package-json branches so the pattern reads as deliberate. The ok branch is intentionally unchanged because checking main after a valid extension resolution is additive, not duplicate.
  • src/cli/update-cli/plugin-payload-validation.test.ts: add a regression case under the existing describe("runPluginPayloadSmokeCheck", …). It writes a package.json with openclaw.extensions: {} (triggers the empty branch) AND main: "dist/index.js" (which doesn't exist on disk), then asserts pkgFailures.length === 1 and that the single failure has reason: "missing-extension-entry". Without the fix this assertion would observe 2 failures.

Diff stat: 2 files, +35 / -0.

Real behavior proof

  • Behavior or issue addressed: Sanitized issue evidence — the loop body at plugin-payload-validation.ts:104-115 is the only branch that pushes a failure and does NOT terminate the iteration. The other branches (missing-package-dir at line ~85, invalid-package-json at line ~98) already use continue; after their failure push.

  • Real environment tested: Local Node 22.x. Probe at /tmp/probe_83891.mjs does both halves of the proof. (a) Parses the patched plugin-payload-validation.ts and verifies the missing-extension-entry branch now contains both the failure push AND a continue; statement, and that the two earlier continue; statements are still present. (b) Replays the loop body logic in pure JS for five scenarios — buggy shape (empty extensions + missing main → 2 duplicate failures, confirming the #83891 symptom), patched shape (same fixture → 1 failure), invalid-extension + missing main (also gated, single failure), empty-extensions + main present (extension failure unchanged), ok-extension + missing main (main check still runs — missing-main-entry emitted as before; no regression on the ok branch).

  • Exact steps or command run after this patch: node /tmp/probe_83891.mjs

  • Evidence after fix:

PASS: missing-extension-entry branch ends with continue; (mirrors missing-package-dir / invalid-package-json branches)
PASS: earlier branch continue; statements (missing-package-dir / invalid-package-json) still present
PASS: replay (buggy): empty extensions + missing main → 2 failure entries for the same pluginId (confirms #83891)
PASS: replay (patched): same fixture → exactly 1 failure (missing-extension-entry); the duplicate main entry is suppressed
PASS: replay (patched, invalid extension): also gated by continue; — single failure entry
PASS: replay (patched, main present): main-on-disk path unchanged — still 1 extension failure, no false main failure
PASS: replay (patched, ok extension + missing main): main check still runs — single missing-main-entry failure as before

ALL CASES PASS
  • Observed result after fix: A plugin with empty openclaw.extensions and a missing main file now emits exactly one smoke failure (missing-extension-entry) instead of two. The ok-extension path is unchanged — the main check still runs against valid extension resolutions, so legitimate missing-main-entry failures still surface.

  • What was not tested: A live update flow exercising runPostCorePluginConvergence end-to-end — that's outside the scope of a single-iteration fall-through fix. The added vitest case exercises the smoke-check boundary directly with a real on-disk fixture.

Audit (per CLAUDE rules — all 5 steps)

  • Existing-helper check: No new helper. The fix is a continue; statement mirroring the pattern already used in two sibling branches (missing-package-dir, invalid-package-json) within the same function. PASS
  • Shared-helper caller check: runPluginPayloadSmokeCheck has two callers — post-core-plugin-convergence.ts (production) and plugin-payload-validation.test.ts (tests). The new behavior strictly reduces the failures array size in the dual-failure edge case and is unchanged in every other case. No caller relies on the duplicate-emission behavior. PASS
  • Broader-fix rival scan: gh pr list --search '83891 in:title,body' and gh pr list --search 'runPluginPayloadSmokeCheck in:title,body' return no open PRs. Issue timeline shows zero cross-references. PASS
  • Recent-merge audit: git log --oneline -5 -- src/cli/update-cli/plugin-payload-validation.ts shows e1061a8b46 test(live): tolerate provider drift in release checks — unrelated. PASS
  • Prototype-pollution scan: N/A — single continue; statement.

Changed files

  • src/cli/update-cli/plugin-payload-validation.test.ts (modified, +29/-0)
  • src/cli/update-cli/plugin-payload-validation.ts (modified, +6/-0)

Code Example

if (extensionResolution.status === "invalid" || extensionResolution.status === "empty") {
      failures.push({
        pluginId,
        installPath,
        reason: "missing-extension-entry",
        ...
      });
    } else if (extensionResolution.status === "ok") {
      ...
    }

    // Only fail on `missing-main-entry` when `main` is *explicitly declared*
    if (typeof manifest.main !== "string" || !manifest.main.trim()) {
      continue;
    }
    ...
    if (!mainStat?.isFile()) {
      failures.push({
        pluginId,
        installPath,
        reason: "missing-main-entry",
        ...
RAW_BUFFERClick to expand / collapse

Severity: low / Confidence: high / Category: bug Triage: confirmed-bug Detected against: openclaw v2026.5.18 (latest stable at time of scan, 2026-05-18) Tooling: clawpatch 0.3.0 + acpx/claude-sonnet-4-5 via Brad Mills protocol

Evidence

  • src/cli/update-cli/plugin-payload-validation.ts:89-118 (runPluginPayloadSmokeCheck)
if (extensionResolution.status === "invalid" || extensionResolution.status === "empty") {
      failures.push({
        pluginId,
        installPath,
        reason: "missing-extension-entry",
        ...
      });
    } else if (extensionResolution.status === "ok") {
      ...
    }

    // Only fail on `missing-main-entry` when `main` is *explicitly declared*
    if (typeof manifest.main !== "string" || !manifest.main.trim()) {
      continue;
    }
    ...
    if (!mainStat?.isFile()) {
      failures.push({
        pluginId,
        installPath,
        reason: "missing-main-entry",
        ...

Reasoning

After the invalid or empty extension branch pushes a failure, there is no continue statement. The loop proceeds to evaluate manifest.main. If main is declared and its file is absent, a second missing-main-entry failure is pushed for the same plugin. In post-core-plugin-convergence.ts each failure becomes a warning and errored: warnings.length > 0 blocks the restart. With two failures for the same plugin the block still fires (correct), but smokeFailures and the per-plugin warning list contain duplicate entries, making diagnostic output confusing. If callers ever count failures to decide severity, the double count would skew the decision.

Reproduction

Provide a plugin whose package.json has no openclaw.extensions field (empty) AND declares a main path that doesn't exist on disk. The smoke check will push two failures for that plugin.

Recommendation

Add continue; immediately after the failures.push inside the invalid/empty branch (before the } else if block) to mirror the pattern used for the earlier missing-package-json and missing-package-dir checks. The ok branch's failure path already falls through correctly because checking main after a valid extension resolution is additive, not duplicate.

Why existing tests miss this

No tests exist for runPluginPayloadSmokeCheck.

Suggested regression test

it('emits exactly one failure for a plugin with empty extensions and missing main', async () => { const result = await runPluginPayloadSmokeCheck({ records: { 'pkg': { source: 'npm', installPath: dir } }, env: {} }); expect(result.failures.filter(f => f.pluginId === 'pkg')).toHaveLength(1); });

Minimum fix scope

Add continue; after the failures.push inside the extensionResolution.status === 'invalid' || 'empty' branch in runPluginPayloadSmokeCheck.


Standardized clawpatch finding. Persistent in v2026.5.18 (not resolved by upgrading from v2026.5.12). Finding ID: fnd_sig-feat-cli-command-0e1f16a0ce-_c41ebbc2f5.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix Smoke check falls through after extension-entry failure, producing duplicate failure entries per plugin [1 pull requests, 1 comments, 2 participants]