Skip to content

feat(api): wire ConduitError into the API error boundary#2527

Merged
devarismeroxa merged 1 commit into
mainfrom
feat/conduiterr-api-boundary
Jul 5, 2026
Merged

feat(api): wire ConduitError into the API error boundary#2527
devarismeroxa merged 1 commit into
mainfrom
feat/conduiterr-api-boundary

Conversation

@devarismeroxa

Copy link
Copy Markdown
Contributor

Step 2 of the structured-error rollout (design: docs/design-documents/20260705-conduit-error-and-structured-output.md; foundation landed in #2524).

The ConduitError foundation now reaches the API. When an error returned to a handler carries a *ConduitError anywhere in its chain, status.go emits its gRPC category plus an additive google.rpc.ErrorInfo detail (stable reason, configPath, suggestion, fix) — so an API/MCP/UI consumer gets a machine-actionable error.

Purely additive

A single integration point (conduitErrorStatus, checked at the top of PipelineError/ConnectorError/ProcessorError/PluginError). Any error that is not a ConduitError falls through to the existing sentinel→code mapping, byte-for-byte unchanged — all existing tests pass. Individual error sources get migrated to emit ConduitError codes incrementally from here (step 3).

Tests

  • A wrapped ConduitError → correct gRPC code + ErrorInfo detail (reason/configPath/suggestion). Proves the compat path end-to-end at the boundary.
  • A sentinel error → maps exactly as before (no detail).

Tier 2 (API boundary, additive, no behavior change for existing errors). Advances #2451 (error codes in the API).

🤖 Generated with Claude Code

Step 2 of the structured-error rollout: the ConduitError foundation now reaches
the API. When an error returned to a gRPC/HTTP handler carries a *ConduitError
(anywhere in its chain), status.go emits its gRPC category plus an additive
google.rpc.ErrorInfo detail carrying the stable reason, configPath, suggestion,
and fix — so an API/MCP/UI consumer gets a machine-actionable error.

This is a single integration point (conduitErrorStatus, checked at the top of
PipelineError/ConnectorError/ProcessorError/PluginError). It is purely additive:
any error that is NOT a ConduitError falls through to the existing sentinel->code
mapping, byte-for-byte unchanged (existing tests still pass). Boundaries get
migrated to emit ConduitError codes incrementally from here.

Tests: a wrapped ConduitError yields the right code + ErrorInfo detail (reason,
configPath, suggestion); a sentinel error maps exactly as before.

Design: docs/design-documents/20260705-conduit-error-and-structured-output.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devarismeroxa devarismeroxa requested a review from a team as a code owner July 5, 2026 21:36
@devarismeroxa devarismeroxa merged commit 15c92ab into main Jul 5, 2026
5 checks passed
@devarismeroxa devarismeroxa deleted the feat/conduiterr-api-boundary branch July 5, 2026 21:42
devarismeroxa added a commit that referenced this pull request Jul 6, 2026
…peline, connector, orchestrator (#2538)

Continues the structured-error rollout (steps 2-4 covered not-found errors in
#2527-#2532) by migrating the non-not-found sentinels used to guard pipeline
lifecycle and mutation preconditions:

- pipeline.running / pipeline.not_running (codes.FailedPrecondition):
  ErrPipelineRunning / ErrPipelineNotRunning, wrapped at their clean
  control-plane raise sites in pkg/lifecycle/service.go (Start/Stop) and
  pkg/orchestrator/{pipelines,connectors,processors}.go (Update/Delete/Create
  guards).
- pipeline.name_already_exists (codes.AlreadyExists) / pipeline.name_missing
  (codes.InvalidArgument): pkg/pipeline/service.go Update and
  validatePipeline (the latter joined via cerrors.Join, same pattern as
  pkg/provisioning/config/validate.go).
- connector.running (codes.FailedPrecondition): the single choke point,
  Instance.Connector in pkg/connector/instance.go.
- connector.invalid_type (codes.InvalidArgument): the user-facing check in
  connector.Service.Create.
- orchestrator.invalid_processor_parent_type (codes.InvalidArgument),
  orchestrator.pipeline_has_{processors,connectors}_attached,
  orchestrator.connector_has_processors_attached,
  orchestrator.immutable_provisioned_by_config (all
  codes.FailedPrecondition): new pkg/orchestrator/codes.go, wrapped at every
  raise site in the orchestrator package.

Every sentinel stays in the error chain via conduiterr.Wrap (invariant
comment at each site); existing errors.Is checks are preserved, though tests
that previously asserted strict `is.Equal(err, sentinel)` identity had to be
updated to errors.Is + a ConduitError code/suggestion check, since wrapping
necessarily changes the concrete error value (documented in the PR).

Deferred (reported, not migrated, to keep blast radius tight): the
lifecycle-poc (PipelineArchV2 preview) mirror of ErrPipelineRunning/
ErrPipelineNotRunning; the internal runPipeline tomb-alive defensive check;
and three defensive/unreachable ErrInvalidConnectorType switch defaults
(SetState, store.decode, Instance.Connector) guarding already-validated
data.

Design: docs/design-documents/20260705-conduit-error-and-structured-output.md


Claude-Session: https://claude.ai/code/session_01Tapg9dLWXKMZJoL65R24vd

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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