Files
dify/api/core/dify_builder/errors.py
T
Charles YaoandClaude Opus 5 b7017aa363 fix(dify-builder): tell a draft that would not start apart from a stale fix
apply_repair's preflight raises PreflightError when the graph it is about
to write would fail at Graph.init. Every handler caught it as a plain
ValueError, so the five apply sites could not tell it apart from a
graph_ops rejection:

- plan approval (Build, Edit) called ANY ValueError "The workflow can't
  start", including "node not found: x", sending the user hunting for a
  broken node that does not exist;
- the repair-apply gates (Build/Edit await_repair, Fix apply) called a
  preflight rejection "The proposed fix no longer applies to the current
  draft" -- the fix applied fine; it would leave an unstartable draft.

Add core DraftWouldNotStartError(ValueError) and make PreflightError
subclass it (services imports core; core still imports no services).
Each site now catches it before the ValueError branch, with the same
control flow and the true reason: plan approval keeps "The workflow
can't start" for it and says "Couldn't apply the workflow" otherwise;
the repair gates say "The workflow can't start" for it and keep
"Couldn't apply the fix" otherwise. The shared card/turn construction is
factored (_graph_not_applied, _change_not_applied, and
drop_unapplied_repair in handlers_fix) and the new literals are
catalogued in strings.py.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 22:57:33 -07:00

44 lines
1.4 KiB
Python

"""Dify Builder domain errors.
Port of dify-enterprise/server/pkg/enterprise/biz/dify_builder/errors.go. Go
models these as sentinel ``error`` values (``errors.New(...)``); Python has
no sentinel-error idiom, so each becomes an ``Exception`` subclass that the
``Repository``/``Runner`` layers raise and callers catch by type.
"""
class ConflictError(Exception):
"""Raised when compare_and_advance loses the version race (stale base_version)."""
class NotFoundError(Exception):
"""Raised when a session/checkpoint/run does not exist."""
class BusyError(Exception):
"""Raised when a session already has an Advance in flight.
The per-session gate is held, so a concurrent SubmitAction is rejected.
"""
class BadRequestError(Exception):
"""A client-supplied value is invalid (e.g. an unresolvable model_config).
Maps to HTTP 400 in services.dify_builder.wiring.dify_builder_error_response.
"""
class ModelUnavailableError(BadRequestError):
"""The chosen or default Builder model cannot be used with its current configuration."""
class DraftWouldNotStartError(ValueError):
"""Raised by the Dify port when the graph it was asked to write would fail
at Graph.init, so nothing was written.
Handlers catch it to say "the workflow can't start" rather than "the
change no longer applies". A ``ValueError`` so older ``except ValueError``
guards around the port's writes still catch it.
"""