Skip to content

fix(deploy): explain "parent must exist" with actionable hint#23

Merged
facundofarias merged 2 commits into
mainfrom
fix/deploy-parent-must-exist-error
Jun 8, 2026
Merged

fix(deploy): explain "parent must exist" with actionable hint#23
facundofarias merged 2 commits into
mainfrom
fix/deploy-parent-must-exist-error

Conversation

@facundofarias

@facundofarias facundofarias commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

dhq deploy surfaces a small-but-persistent deployhq api: 422 parent must exist bucket on Mixpanel (5 hits / 30d on v0.18.1, all humans, all on deploy). The Rails Deployment model validates that the new deploy's start_revision traces back to a prior deployment's end_revision on the same target. When it can't, the user sees an opaque message — they don't know what "parent" means, what SHA was sent, or that --full would bypass the issue.

Two real-world causes:

  • Force-push / history rewrite removed the SHA from the repo, but the server's LastRevision still points at it → incremental can't trace back.
  • User pastes a random SHA into --start-revision (e.g. from dhq repos commits), not realizing it must be a previously-deployed SHA, not any commit.

What this PR does

  • internal/commands/deploy.go — new translateParentMustExistError helper detects 422 + "parent must exist" on either APIError.Message or APIError.Errors[] and rewrites it as a UserError. The actual start_revision SHA is in the headline line (so telemetry.SanitizeErrorMessage preserves it for the dashboard), and the Hint offers --full or a corrected --start-revision referencing dhq deployments list. Wired into both call sites: PreviewDeployment (dry-run) and CreateDeployment.
  • internal/commands/deploy_test.go — four unit tests:
    • rewrites from .Message
    • rewrites from .Errors array (the more common Rails shape)
    • empty start_revision renders as (none) so users can tell it was empty
    • passthrough table locking down that unrelated errors stay untouched (nil, non-API, different 422 validation, parent-must-exist text on a non-422)

What this PR deliberately doesn't do

  • Auto-retry with --full: too magical; could surprise a user by deploying way more than intended.
  • Preflight check via ListDeployments: adds an API call to every deploy for a small failure bucket; not worth it.
  • Embed start_revision in dhq deploy --dry-run output: separate UX improvement, easy follow-up if useful.

Test plan

  • go test ./... — all packages green
  • golangci-lint run ./... — 0 issues
  • Tests cover both API error shapes (.Message and .Errors[]), the empty-SHA rendering, and passthrough behaviour

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved deployment error messages when a start revision can't be matched — now show the provided revision (explicitly marking empty as “(none)”), offer actionable hints (likely causes and remediation including use of --full and --start-revision), and apply consistently for both preview and create flows.
  • Tests

    • Added tests covering the rewritten messages and ensuring unrelated errors remain unchanged.

Telemetry shows a small but persistent "deployhq api: 422 parent must
exist" bucket (5 hits / 30d on v0.18.1, all humans, all on `deploy`).
The API enforces that a deployment's start_revision must trace back to
a prior deployment's end_revision on the same target; when it can't,
the CLI surfaces the raw Rails validation message which is opaque
unless you've read the source.

- New translateParentMustExistError detects the 422 + "parent must
  exist" combo from APIError.Message or .Errors and rewrites it as a
  UserError that names the actual start_revision SHA on the headline
  line (so telemetry's SanitizeErrorMessage preserves it) and offers
  --full or a corrected --start-revision in the Hint.
- Apply at both deploy call sites (PreviewDeployment for --dry-run and
  CreateDeployment for the real path).
- Four unit tests cover the .Message variant, the .Errors-array variant,
  the empty-start_revision rendering, and a passthrough table that locks
  in that unrelated errors stay untouched (nil, non-API, other 422
  validation, the same string on a non-422).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2cfbd083-9423-43eb-8831-c390c6503095

📥 Commits

Reviewing files that changed from the base of the PR and between 7b723f3 and 993b79e.

📒 Files selected for processing (2)
  • internal/commands/deploy.go
  • internal/commands/deploy_test.go

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


Walkthrough

Detects API HTTP 422 "parent must exist" validation errors during deploy preview/create, rewrites them into an actionable output.UserError (including (none) when start revision is empty) and integrates this translation into both dry-run preview and real deployment paths.

Changes

Parent Must Exist Error Translation

Layer / File(s) Summary
Import update
internal/commands/deploy.go
Adds Go errors import used for API error inspection.
Error translation helpers and tests
internal/commands/deploy.go, internal/commands/deploy_test.go
Adds translateParentMustExistError and mentionsParentMustExist to detect HTTP 422 "parent must exist" in sdk.APIError (checking both Message and Errors array) and rewrite it into an output.UserError with --full/--start-revision hints; tests cover message-field and Errors-array rewriting, explicit (none) for empty start_revision, and pass-through of unrelated errors.
Deployment command integration
internal/commands/deploy.go
Dry-run preview and actual deployment creation paths now call translateParentMustExistError(err, req.StartRevision) before returning errors from client.PreviewDeployment / client.CreateDeployment.
sequenceDiagram
  participant CLI as CLI (deploy command)
  participant SDK as SDK client
  participant API as API
  CLI->>SDK: PreviewDeployment/CreateDeployment(req)
  SDK->>API: HTTP request (deployment create/preview)
  API-->>SDK: 422 validation error ("parent must exist")
  SDK-->>CLI: sdk.APIError (422)
  CLI->>CLI: translateParentMustExistError(err, start_revision)
  CLI-->>User: output.UserError (translated message + hints)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

🚥 Pre-merge checks | ✅ 4
✅ 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 'fix(deploy): explain "parent must exist" with actionable hint' directly matches the core change: translating an opaque API error into a user-friendly message with actionable guidance.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/deploy-parent-must-exist-error

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

…t-exist-error

# Conflicts:
#	internal/commands/deploy.go
#	internal/commands/deploy_test.go
@facundofarias facundofarias merged commit a6480be into main Jun 8, 2026
2 of 3 checks passed
@facundofarias facundofarias deleted the fix/deploy-parent-must-exist-error branch June 8, 2026 09:20
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