Skip to content

fix: classify invalid CLI input as validation errors - #2267

Open
evandance wants to merge 1 commit into
mainfrom
refactor/error-classification
Open

fix: classify invalid CLI input as validation errors#2267
evandance wants to merge 1 commit into
mainfrom
refactor/error-classification

Conversation

@evandance

@evandance evandance commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Invalid CLI input surfaced by Cobra could be emitted as internal/unknown (exit 5), telling agents the CLI failed when they only needed to fix the command. Classification depended on error wording, so new validators and Cobra wording changes could silently be misclassified. This PR classifies errors at their source and keeps that result consistent across rendering and lifecycle hooks.

Changes

Error source Result
Cobra Args, command discovery, required/group flags, and lazy completion arguments validation/invalid_argument
Untyped failures from PersistentPreRunE, PreRunE, RunE, PostRunE, PersistentPostRunE, and plugin wrappers internal/unknown
Framework setup or output failures, including credential-provider lookup and --version writes internal/unknown
Existing typed errors, BareError, and PartialFailureError Preserved unchanged
  • Instrument the final command tree after plugin hooks and custom help are installed. Classification is stateless, so repeated Execute calls and separate command trees cannot contaminate one another.
  • Make shortcut positional-argument failures actionable by naming every stray word and pointing at the command's flags. param stays unset, because a shortcut declares no positional parameter for a stray word to belong to.
  • Preserve the producer's classification if envelope rendering fails. Settle stderr and the exit code before Shutdown, and give each lifecycle handler its own copy of the error where the SDK owns the value; a wrapped chain is shared whole so errors.Is and errors.As still reach the producer's sentinels.

Test Plan

  • Identical errors from Args and all five error-returning execution hooks classify by origin and preserve their causes.
  • Cover required/group flags, repeated execution, custom help, lazy __complete, plugin short-circuits, --version write failures, and silent short writes.
  • Cover shortcut diagnostics, render fallback, credential-provider classification, and pass-through/lifecycle-copy contracts for typed errors, BareError, PartialFailureError, wrapped chains, and extension-defined typed errors.
  • Local gates: make unit-test, make vet, make fmt-check, go mod tidy, golangci-lint, repository contract lint, lint module tests, make build, and make quality-gate.

Related Issues

  • None

@evandance
evandance requested a review from liangshuo-1 as a code owner August 10, 2026 09:59
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The CLI now tracks whether failures occur during user-input validation or command execution. It normalizes untyped errors, passes isolated typed snapshots to shutdown hooks, preserves exit-code-only signals, and adds typed positional-argument and credential-provider errors.

Changes

Dispatch Error Classification

Layer / File(s) Summary
Instrument Cobra dispatch stages
cmd/error_stage.go, cmd/build.go
The command tree tracks validation and command-body entry. Positional validator errors become typed validation errors. Subcommands are instrumented before plugin hooks.
Normalize and render root errors
cmd/root.go, cmd/root_integration_test.go
Root execution normalizes errors by dispatch stage, passes cloned typed errors to shutdown hooks, and rebuilds typed errors when envelope serialization fails.
Classify command and shortcut failures
shortcuts/common/runner.go, internal/cmdutil/factory.go, shortcuts/common/runner_args_test.go
Shortcut positional failures and credential-provider lookup failures now return typed errors with causes and contextual diagnostics.
Validate classification and lifecycle behavior
cmd/error_stage_test.go, cmd/root_test.go
Tests cover validation and command-body classification, shutdown error isolation, exit-code-only signals, typed rendering fallback, and normalized error causes.
Document shutdown error propagation
extension/platform/lifecycle.go, extension/platform/README.md
Lifecycle documentation defines shutdown error snapshots, classification, excluded failure paths, and exit-code-only results.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CobraDispatch
  participant ErrorStageState
  participant RootErrorHandler
  participant ShutdownHook
  participant ErrorRenderer
  CobraDispatch->>ErrorStageState: reset and mark validation or command-body stage
  CobraDispatch->>RootErrorHandler: return dispatch error
  RootErrorHandler->>RootErrorHandler: normalize error by stage
  RootErrorHandler->>ShutdownHook: provide cloned typed error snapshot
  RootErrorHandler->>ErrorRenderer: render classified error
  ErrorRenderer-->>RootErrorHandler: report envelope serialization failure
  RootErrorHandler->>ErrorRenderer: render rebuilt typed error
Loading

Possibly related PRs

  • larksuite/cli#1837: Both changes modify Cobra dispatch and typed error handling.
  • larksuite/cli#2232: Both changes modify the command construction and execution pipeline in cmd/build.go and cmd/root.go.

Suggested labels: bugfix

Suggested reviewers: liangshuo-1

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description follows the template and clearly documents the motivation, changes, verification steps, and related issues.
Title check ✅ Passed The title is concise and accurately summarizes the main change: classifying invalid CLI input as validation errors.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/error-classification

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.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Aug 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
cmd/error_stage_test.go (1)

316-318: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the exact exit code.

rebuildTypedError with stageCommandBody produces an internal error, so the exit code is deterministic. Compare against output.ExitInternal instead of only excluding 1.

♻️ Proposed stronger assertion
-	if exit == 1 {
-		t.Errorf("exit = 1; the typed category's exit code must survive the rewrite")
-	}
+	if exit != int(output.ExitInternal) {
+		t.Errorf("exit = %d, want %d; the typed category's exit code must survive the rewrite",
+			exit, int(output.ExitInternal))
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/error_stage_test.go` around lines 316 - 318, Update the assertion in the
rebuildTypedError test to require exit to equal output.ExitInternal, replacing
the current check that only rejects 1. Keep the existing error message context
while asserting the deterministic internal-error exit code produced with
stageCommandBody.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/common/runner.go`:
- Around line 1238-1241: Update the validation error construction in the
positional-argument handling path to pass args[0] to the %q formatter instead of
the full args slice. Keep WithParam(args[0]) and the existing hint unchanged so
the message and parameter consistently report the first stray argument.

---

Nitpick comments:
In `@cmd/error_stage_test.go`:
- Around line 316-318: Update the assertion in the rebuildTypedError test to
require exit to equal output.ExitInternal, replacing the current check that only
rejects 1. Keep the existing error message context while asserting the
deterministic internal-error exit code produced with stageCommandBody.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f48227e1-359c-4a58-937e-01c00f04c684

📥 Commits

Reviewing files that changed from the base of the PR and between 2016120 and 57d80e1.

📒 Files selected for processing (11)
  • cmd/build.go
  • cmd/error_stage.go
  • cmd/error_stage_test.go
  • cmd/plugin_integration_test.go
  • cmd/root.go
  • cmd/root_integration_test.go
  • cmd/root_test.go
  • extension/platform/README.md
  • extension/platform/lifecycle.go
  • internal/cmdutil/factory.go
  • shortcuts/common/runner.go

Comment thread shortcuts/common/runner.go Outdated
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@e829dd6ad2d9f4dd7374dd56efd733c247f44a9e

🧩 Skill update

npx skills add larksuite/cli#refactor/error-classification -y -g

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.34513% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.45%. Comparing base (b6d0473) to head (e829dd6).

Files with missing lines Patch % Lines
internal/hook/emit.go 93.33% 1 Missing and 1 partial ⚠️
cmd/root.go 94.44% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2267      +/-   ##
==========================================
+ Coverage   76.43%   76.45%   +0.02%     
==========================================
  Files        1046     1047       +1     
  Lines      115162   115251      +89     
==========================================
+ Hits        88022    88114      +92     
+ Misses      20377    20374       -3     
  Partials     6763     6763              

☔ View full report in Codecov by Harness.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@evandance
evandance force-pushed the refactor/error-classification branch from 57d80e1 to b929eaa Compare August 10, 2026 10:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/common/runner_args_test.go`:
- Around line 46-60: Extend the validation-error assertions in the runner
argument test using errs.ProblemOf(err) to verify Category is
errs.CategoryValidation and Subtype is errs.SubtypeInvalidArgument. Retain
errors.As into *errs.ValidationError and the existing Param assertion for the
first stray word, since ProblemOf does not expose Param.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c5f45baf-4411-4997-83b4-92b1250bbc00

📥 Commits

Reviewing files that changed from the base of the PR and between 57d80e1 and b929eaa.

📒 Files selected for processing (1)
  • shortcuts/common/runner_args_test.go

Comment thread shortcuts/common/runner_args_test.go
@evandance
evandance force-pushed the refactor/error-classification branch from b929eaa to ccb620a Compare August 10, 2026 10:54

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
cmd/error_stage_test.go (1)

241-249: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Probe more than one rejected argument shape.

This walk only detects validators that reject ten arguments. A lower-bound validator, or a custom validator that rejects an empty or single-argument input, can remain unwrapped without failing this test. Probe empty, single, and oversized argument lists, then require typed errors for every rejected probe.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/error_stage_test.go` around lines 241 - 249, Expand the
argument-validation probes in the walk around c.Args to test empty,
single-argument, and oversized argument lists instead of only ten arguments. For
every rejected probe, increment the check and require errs.ProblemOf(err) to
succeed, recording c.CommandPath() as unguarded when it does not; preserve the
existing traversal behavior for accepted inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/error_stage_test.go`:
- Around line 359-369: Update the test around normalizeRootError to store the
raw errors passed for stageUserInput and stageCommandBody, then assert errors.Is
for each normalized result against its corresponding raw error. Keep the
existing ProblemOf classification checks and ensure both normalization paths
verify cause preservation.
- Around line 107-120: Update the test around the tamper plugin’s Shutdown
handler to maintain an invocation counter, increment it inside the handler, and
assert after executeWithCapturedOS returns that the counter equals one. Preserve
the existing error-rewrite behavior and exit-code assertions.
- Around line 319-321: Update the assertion in the error-stage test to compare
exit against output.ExitCodeOf(broken), ensuring the rewritten error preserves
the typed category’s exact exit code rather than merely checking that it is not
1.

---

Nitpick comments:
In `@cmd/error_stage_test.go`:
- Around line 241-249: Expand the argument-validation probes in the walk around
c.Args to test empty, single-argument, and oversized argument lists instead of
only ten arguments. For every rejected probe, increment the check and require
errs.ProblemOf(err) to succeed, recording c.CommandPath() as unguarded when it
does not; preserve the existing traversal behavior for accepted inputs.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 70984ba7-459e-4504-bfcc-ac6047d8a3f1

📥 Commits

Reviewing files that changed from the base of the PR and between b929eaa and ccb620a.

📒 Files selected for processing (1)
  • cmd/error_stage_test.go

Comment thread cmd/error_boundary_test.go
Comment thread cmd/error_stage_test.go Outdated
Comment thread cmd/error_stage_test.go Outdated
@evandance
evandance force-pushed the refactor/error-classification branch from ccb620a to 682bfba Compare August 10, 2026 11:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/error_stage_test.go`:
- Around line 244-257: Update the rejecting-validator assertions in the
forEachCommand test to require errs.CategoryValidation and
errs.SubtypeInvalidArgument, rather than accepting any errs.Problem. When err
can be unwrapped as *errs.ValidationError, use errors.As to assert its Param
field as required; preserve the existing tracking of unclassified errors and
command paths.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b013e74-09f6-49a7-a03a-120400ba3ccf

📥 Commits

Reviewing files that changed from the base of the PR and between ccb620a and 682bfba.

📒 Files selected for processing (2)
  • cmd/error_stage_test.go
  • shortcuts/common/runner_args_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/common/runner_args_test.go

Comment thread cmd/error_boundary_test.go
@evandance
evandance force-pushed the refactor/error-classification branch 5 times, most recently from a7ec4d7 to 52390c3 Compare August 11, 2026 11:40
@evandance evandance changed the title fix: report command-line mistakes as user errors, not internal faults fix: classify invalid CLI input as validation errors Aug 11, 2026
@evandance
evandance force-pushed the refactor/error-classification branch 3 times, most recently from 7be353d to 9506ad9 Compare August 17, 2026 07:23
Cobra surfaced several command-line validation failures as plain errors.
Classifying them by message text could report correctable input as
internal/unknown, misleading agents and returning the wrong exit code.

Classify errors at the boundary that produces them. Args and residual
Cobra validation become validation/invalid_argument, while raw execution
hooks and plugin failures become internal/unknown. Preserve typed errors,
causes, bare exits, and partial failures.

Make final-tree instrumentation stateless, type pre-callback framework
failures at their source, and keep rendering and Shutdown lifecycle
observations consistent. A Shutdown handler receives the error the command
returned with its wrapping intact, so errors.Is still reaches the
producer's sentinels. Add regression coverage for repeated execution,
late help, lazy completion, writer failures, shortcut diagnostics,
credential-provider classification, and lifecycle isolation.
@evandance
evandance force-pushed the refactor/error-classification branch from 9506ad9 to e829dd6 Compare August 17, 2026 11:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant