Skip to content

fix(cli): fix threshold commands (batch body, help example, decimal parsing, exit code, error msg, balance mapping)#15154

Merged
tonypan2 merged 1 commit intomainfrom
tonypan/fix-installation-threshold-body
Feb 23, 2026
Merged

fix(cli): fix threshold commands (batch body, help example, decimal parsing, exit code, error msg, balance mapping)#15154
tonypan2 merged 1 commit intomainfrom
tonypan/fix-installation-threshold-body

Conversation

@tonypan2
Copy link
Copy Markdown
Contributor

@tonypan2 tonypan2 commented Feb 19, 2026

Summary

  • Fix API contract mismatch: CLI sent { items: [{...}] } but batch threshold API expects bare JSON array [{...}]. Installation-level threshold creation was completely broken.
  • Changed return type from { store: { id: string } } to void (API returns 204 No Content)
  • Fix help text example 100 50 200050 100 2000 (old example violated minimum <= spend validation)
  • Fix spend and limit parsing: parseIntparseFloat (was silently truncating decimals)
  • Add Math.round after parseFloat * 100 to prevent floating point precision issues
  • Fix "resource not found" exit code: return 0return 1
  • Fix error message: said "Spend" when validating Minimum
  • Fix balance mapping: threshold key used resourceName instead of resourceId

Before / After

1. API request body (critical fix)

The batch threshold endpoint expects a bare JSON array. The CLI was wrapping it in { items: [...] }, causing all installation-level threshold creation to fail.

Before — what the CLI sent:

POST /v1/integrations/installations/:id/billing/threshold/batch

{
  "items": [
    {
      "billingPlanId": "plan_123",
      "minimumAmountInCents": 5000,
      "purchaseAmountInCents": 10000,
      "maximumAmountPerPeriodInCents": 200000,
      "metadata": "{}"
    }
  ]
}

After — correct format:

POST /v1/integrations/installations/:id/billing/threshold/batch

[
  {
    "billingPlanId": "plan_123",
    "minimumAmountInCents": 5000,
    "purchaseAmountInCents": 10000,
    "maximumAmountPerPeriodInCents": 200000,
    "metadata": "{}"
  }
]

2. Decimal parsing

BeforeparseInt silently truncated decimals:

$ vercel ir create-threshold my-resource 10.10 20.50 100.99
# minimum = parseInt("10.10") * 100 = 10 * 100 = 1000 ($10.00, not $10.10)
# spend   = parseInt("20.50") * 100 = 20 * 100 = 2000 ($20.00, not $20.50)
# limit   = parseInt("100.99") * 100 = 100 * 100 = 10000 ($100.00, not $100.99)

AfterparseFloat + Math.round gives correct cents:

$ vercel ir create-threshold my-resource 10.10 20.50 100.99
# minimum = Math.round(parseFloat("10.10") * 100) = Math.round(1010.0000...01) = 1010 ($10.10 ✓)
# spend   = Math.round(parseFloat("20.50") * 100) = Math.round(2050.0) = 2050 ($20.50 ✓)
# limit   = Math.round(parseFloat("100.99") * 100) = Math.round(10099.0) = 10099 ($100.99 ✓)

3. Help text example

Before — violated minimum <= spend validation (100 > 50):

vercel integration-resource create-threshold my-acme-resource 100 50 2000
# → Error: Minimum cannot be greater than spend.

After — valid example:

vercel integration-resource create-threshold my-acme-resource 50 100 2000
# minimum=$50, spend=$100, limit=$2000 ✓

4. Resource not found exit code

Before:

$ vercel ir create-threshold nonexistent 50 100 2000
> The resource nonexistent was not found.    # output.log (no "Error:" prefix)
$ echo $?
0    # success?!

After:

$ vercel ir create-threshold nonexistent 50 100 2000
Error: The resource nonexistent was not found.    # output.error
$ echo $?
1    # correct failure exit code

5. Error message copy

Before:

$ vercel ir create-threshold my-resource abc 100 2000
Error: Minimum is an invalid number format. Spend must be a positive number (ex. "5.75")
#                                           ^^^^^ wrong field name

After:

$ vercel ir create-threshold my-resource abc 100 2000
Error: Minimum is an invalid number format. Minimum must be a positive number (ex. "5.75")
#                                           ^^^^^^^ correct

6. Balance mapping key collision

When merging thresholds into the balance mapping, the else branch keyed by display name (resourceName) instead of resourceId. This caused lookups to fail when iterating the map.

Before:

// threshold for resourceId="res_abc" with display name "my-db"
mappings["my-db"] = { threshold, resourceName: "my-db" };
// but balance was stored under mappings["res_abc"]
// → threshold never merged with its balance

After:

// both balance and threshold keyed by resourceId
mappings["res_abc"] = { threshold, resourceName: "my-db" };
// → threshold correctly merges with balance entry

Test plan

Unit Tests

$ cd packages/cli && pnpm vitest run test/unit/commands/integration-resource/create-threshold.test.ts test/unit/commands/integration/balance.test.ts

 ✓ test/unit/commands/integration/balance.test.ts  (14 tests) 74ms
 ✓ test/unit/commands/integration-resource/create-threshold.test.ts  (29 tests) 112ms

 Test Files  2 passed (2)
      Tests  43 passed (43)

Test updates:

  • Fixed create-threshold.test.ts: exit code assertion 01 for "resource not found"
  • Fixed create-threshold.test.ts: error message assertion "Spend must be" → "Minimum must be"
  • Added test: batch endpoint body is a bare JSON array (not { items: [...] })
  • Added test: Math.round produces exact cents for floats like 5.75575

Manual Testing

Test Command Expected Actual
Help example order vc integration-resource create-threshold --help Examples show 50 100 2000 my-acme-resource 50 100 2000
Resource not found prefix vc ir create-threshold nonexistent-resource 50 100 2000 Error: prefix Error: The resource nonexistent-resource was not found.
Resource not found exit code (same) Exit code 1 EXIT_CODE=1
Invalid minimum message vc ir create-threshold nonexistent abc 100 2000 "Minimum must be" Minimum must be a positive number
Float parsing works vc ir create-threshold nonexistent 5.75 10.99 1000.50 Parsing succeeds ✅ Reaches resource lookup (no parse error)

Code paths covered

Path Verified by
Batch endpoint sends bare array [...] not { items: [...] } Unit test (body assertion)
Math.round() on float-to-cents conversion Unit test (575 not 574.99) + manual
Resource not found → output.error + return 1 Unit test + manual
Error message: "Minimum must be" (not "Spend") Unit test + manual
Help example order 50 100 2000 Manual (--help output)
Balance mapping uses resourceId key Unit test (multi-resource scenario)
Balance --format=json output Unit test (14 balance tests)

Not manually testable (no prepayment integrations on test team): actual threshold creation via batch endpoint, balance display with real prepayment data. Covered by unit tests with mocks.

🤖 Generated with Claude Code

Warning

High Risk Change

CLI bug fixes for billing threshold commands including API body format, decimal parsing with Math.round, exit codes, and error messages - all changes are correctness fixes with comprehensive test coverage.

  • Billing: fixes API request body format from { items: [...] } to bare JSON array
  • Billing: changes parseInt to parseFloat with Math.round for correct cents conversion
  • Fixes exit code from 0 to 1 for resource-not-found error case

Risk assessment for commit cb68d69.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Feb 19, 2026

🦋 Changeset detected

Latest commit: 6ed26f8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
vercel Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 19, 2026

📦 CLI Tarball Ready

The Vercel CLI tarball for this PR is now available!

Quick Test

You can test this PR's CLI directly by running:

npx https://zero-config-qp9csboes-uncurated-tests.vercel.app/tarballs/vercel.tgz --help

Use in vercel.json

To use this CLI version in your project builds, add to your vercel.json:

{
  "build": {
    "env": {
      "VERCEL_CLI_VERSION": "vercel@https://zero-config-qp9csboes-uncurated-tests.vercel.app/tarballs/vercel.tgz"
    }
  }
}

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 19, 2026

🧪 Unit Test Strategy

Comparing: 984d4486ed26f8 (view diff)

Strategy: Affected packages only

✅ Only testing packages that have been modified or depend on modified packages.

Affected packages - 1 (3%)
  1. vercel
Unaffected packages - 39 (98%)
  1. @vercel-internals/get-package-json
  2. @vercel/backends
  3. @vercel/build-utils
  4. @vercel/cervel
  5. @vercel/cli-auth
  6. @vercel/client
  7. @vercel/config
  8. @vercel/detect-agent
  9. @vercel/edge
  10. @vercel/elysia
  11. @vercel/error-utils
  12. @vercel/express
  13. @vercel/fastify
  14. @vercel/firewall
  15. @vercel/frameworks
  16. @vercel/fs-detectors
  17. @vercel/functions
  18. @vercel/gatsby-plugin-vercel-builder
  19. @vercel/go
  20. @vercel/h3
  21. @vercel/hono
  22. @vercel/hydrogen
  23. @vercel/koa
  24. @vercel/nestjs
  25. @vercel/next
  26. @vercel/node
  27. @vercel/oidc
  28. @vercel/oidc-aws-credentials-provider
  29. @vercel/python
  30. @vercel/python-analysis
  31. @vercel/redwood
  32. @vercel/related-projects
  33. @vercel/remix-builder
  34. @vercel/routing-utils
  35. @vercel/ruby
  36. @vercel/rust
  37. @vercel/static-build
  38. @vercel/static-config
  39. examples

Results

  • Unit tests: Only affected packages will run unit tests
  • E2E tests: Running in parallel via E2E Tests workflow
  • Type checks: Only affected packages will run type checks

This comment is automatically generated based on the affected testing strategy

@tonypan2 tonypan2 changed the title fix(cli): send bare JSON array for installation threshold batch endpoint fix(cli): send bare array for installation threshold batch endpoint Feb 19, 2026
Copy link
Copy Markdown
Contributor Author

@tonypan2 tonypan2 force-pushed the tonypan/fix-installation-threshold-body branch from 0d5b783 to e0645dd Compare February 19, 2026 23:50
@tonypan2 tonypan2 requested a review from bhrigu123 February 20, 2026 01:38
@tonypan2 tonypan2 marked this pull request as ready for review February 20, 2026 01:38
@tonypan2 tonypan2 requested review from a team as code owners February 20, 2026 01:38
@tonypan2 tonypan2 force-pushed the tonypan/fix-installation-threshold-body branch from e0645dd to 5eaa87f Compare February 20, 2026 02:27
@tonypan2 tonypan2 changed the title fix(cli): send bare array for installation threshold batch endpoint fix(cli): fix threshold commands (batch body, help example, decimal parsing, exit code, error msg, balance mapping) Feb 20, 2026
@tonypan2 tonypan2 force-pushed the tonypan/fix-installation-threshold-body branch from 5eaa87f to cb68d69 Compare February 20, 2026 18:18
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tonypan2 tonypan2 force-pushed the tonypan/fix-installation-threshold-body branch from cb68d69 to 6ed26f8 Compare February 20, 2026 23:21
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Feb 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
zero-config-go Ready Ready Preview, Comment Feb 20, 2026 11:22pm

@tonypan2 tonypan2 merged commit 3969d91 into main Feb 23, 2026
301 of 303 checks passed
@tonypan2 tonypan2 deleted the tonypan/fix-installation-threshold-body branch February 23, 2026 21:06
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.

1 participant