fix(gateway): classify Photon target_not_allowed as a dead target - #74145
Open
pierrenode wants to merge 1 commit into
Open
fix(gateway): classify Photon target_not_allowed as a dead target#74145pierrenode wants to merge 1 commit into
pierrenode wants to merge 1 commit into
Conversation
teknium1
reviewed
Jul 30, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for tracing the Photon error classification path. The classifier gap is real: PhotonAdapter._sidecar_send() preserves str(e) (plugins/platforms/photon/adapter.py:2473-2482), and that string includes target_not_allowed (plugins/platforms/photon/adapter.py:262-282).
Problems
- The claimed cron fix does not reach production cron delivery.
cron/scheduler.py:1789-1806callsrouter._deliver_to_platform(...)directly. Dead-target lookup andmark_dead()only occur inDeliveryRouter.deliver()(gateway/delivery.py:341-385), so the new branch ingateway/platforms/base.pycannot short-circuit a later cron tick. - The added regression test calls
router.deliver()(tests/gateway/test_dead_targets.py:79-85), covering the wrapper that cron bypasses rather than the reported scheduler path.
Suggested changes
- Route the scheduler's live and fallback delivery paths through a dead-target-aware public API, while preserving its existing topic-routing and fallback semantics.
- Add a scheduler-level regression covering two Photon cron deliveries and asserting the second does not invoke the live adapter or standalone fallback.
Automated hermes-sweeper review.
| @@ -2328,6 +2328,7 @@ def classify_send_error(exc: Optional[BaseException], error_text: str = "") -> s | |||
| or "not enough rights" in blob | |||
| or "have no rights" in blob | |||
| or "not a member" in blob | |||
| or "target_not_allowed" in blob | |||
Contributor
There was a problem hiding this comment.
Blocking: cron does not call the wrapper that consumes this classification. cron/scheduler.py:1789-1806 invokes _deliver_to_platform() directly, while mark_dead() is only reached from DeliveryRouter.deliver() at gateway/delivery.py:376-385; this match therefore will not short-circuit a later cron tick.
pierrenode
force-pushed
the
fix/photon-dead-target-classification
branch
from
August 11, 2026 07:43
273f3bf to
2898814
Compare
classify_send_error() didn't recognize Photon's target_not_allowed error text (shared/free-tier lines permanently rejecting a new outbound thread), so DeliveryRouter.deliver() never called mark_dead() for it and every delivery attempt kept re-sending to a target the sidecar had already rejected — wasting a send against flood control on each try. Add the 'target_not_allowed' substring to the existing 'forbidden' bucket (already documented as covering 'lacks permission to post to the target'). No new error kind, no change to DeadTargetRegistry._DEAD_ERROR_KINDS — mirrors the existing pattern for other permanently-rejected targets. The scheduler-level wiring this PR originally also carried (cron's _deliver_result() calling DeliveryRouter._deliver_to_platform() directly, bypassing deliver()'s dead-target check) is intentionally left to NousResearch#64915, which already lands a broader fix for that same call site (including the media-only-job case this PR's version didn't cover) — landing both would conflict on the same lines with divergent implementations.
pierrenode
force-pushed
the
fix/photon-dead-target-classification
branch
from
August 13, 2026 23:39
2898814 to
84430d1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PhotonAdapter._sidecar_send()preservesstr(e)which includestarget_not_allowedtext when a shared/free-tier Photon line permanently cannot open a new outbound thread to a target.classify_send_error()didn't recognize this shape, somark_dead()was never called and the target retried forever on every send.classify_send_error()now recognizestarget_not_allowedas a"forbidden"-class dead target — matching the existing docstring, which already describes this bucket as "lacks permission to post to the target".Scope note (updated)
An earlier revision of this PR also carried scheduler-level wiring (
cron/scheduler.py::_deliver_result()consulting/updating its ownDeadTargetRegistryaround the live-adapter and standalone paths, since production cron delivery callsDeliveryRouter._deliver_to_platform()directly and never went throughDeliveryRouter.deliver()'s dead-target check). That wiring has been removed from this PR: it duplicates — with a narrower implementation (no media-only-job coverage, no shared registry passed intoDeliveryRouter) — the fix already carried by #64915, which is intentionally the PR that owns that call site. Landing both would conflict on the same lines with divergent implementations.This PR is back to its original, narrow scope: only the
classify_send_error()classification fix.Test plan
tests/gateway/test_dead_targets.py::test_photon_target_not_allowed_marks_target_dead—DeliveryRouter.deliver()-level test proving the classifier recognizes the Photon error shape end-to-end (first delivery marks dead, second is skipped).tests/gateway/test_send_error_classification.py— unit coverage for the newtarget_not_allowed→"forbidden"classification.gateway/platforms/base.pychange breaks both of the above.tests/gateway/test_dead_targets.py+tests/gateway/test_send_error_classification.py+tests/gateway/test_send_multiple_images.py(37 passed) — all green.ruff checkclean on all changed files.upstream/main, single commit.