cloudflare/pingora · error · panic
Custom proxy task API not implemented
Error message
Custom proxy task API not implemented
What it means
`write_proxy_tasks` is a default method on the `Custom` session trait in pingora-core that unconditionally panics. It exists so implementors of custom protocols only pay for the proxy-task API if they use it: the default body panics with "Custom proxy task API not implemented" to signal that this custom session does not support queuing HTTP proxy tasks. The panic is documented and expected whenever the proxy-task path is exercised on a session that never overrode the method.
Solutions
- Do not drive custom-protocol sessions through the HTTP proxy-task API; handle them with the custom session's own read/write methods and return the response directly.
- If proxy tasks are needed, override `write_proxy_tasks` (and related proxy-task methods) in your `Custom` trait implementation with a real protocol-specific implementation.
- Ensure the server/handoff logic selects the HTTP session type (h1/h2) rather than the custom session type when proxy-task behavior is required.
Defensive patterns
Strategy: type-guard
Validate before calling
// Before routing a session through proxy-task APIs, confirm it is not a Custom session
fn supports_proxy_tasks(session: &Session) -> bool {
!matches!(session.as_downstream(), /* custom protocol session */ _ if is_custom(session))
} Type guard
fn is_custom_session(s: &Session) -> bool {
// narrow by the concrete downstream/session enum variant for the custom protocol
matches!(s.as_downstream(), DownstreamSession::Custom(_))
} Prevention
- Never route custom-protocol connections through the generic HTTP proxy-task pipeline.
- If your protocol needs proxy tasks, override `write_proxy_tasks` and the related proxy-task trait methods.
- Check the configured protocol type (http(s) vs custom) before selecting the proxy code path.
- Pin and review Pingora upgrades for new calls into default trait methods on custom sessions.
When it happens
Trigger: Using a `custom` protocol session (from `pingora-core/src/protocols/http/custom/server.rs`) in a context that calls `write_proxy_tasks` — e.g., routing a custom-protocol connection through HTTP proxy task writing — without the trait implementor overriding `write_proxy_tasks` with a real implementation.
Common situations: Configuring a Pingora app with a custom protocol listener but letting the generic proxy pipeline (which expects HTTP proxy tasks) drive the session; upgrading Pingora and new proxy-task code paths now reaching the custom session default; implementing `Custom` for a protocol that has no notion of HTTP proxy tasks and then using it with `proxy_to_upstream`-style APIs.
Related errors
- body buf exists once a chunk was parsed out of it
- body buf exists once a partial chunk head was buffered
- body buf is initialized before reading trailers
- cache_key_callback must be implemented when caching is…
- Failed to build listeners
AI-assisted analysis of cloudflare/pingora@4487f7b2ab (2026-09-13).
Data as JSON: /api/errors/38668895786ad471.
Report an issue: GitHub.
Appendix: source
Thrown at pingora-core/src/protocols/http/custom/server.rs:90
///
/// # Panics
/// Panics if the Custom session does not implement the proxy task API.
#[track_caller]
fn send_proxy_task(&mut self, _task: HttpTask) {
panic!("Custom proxy task API not implemented")
}
/// Whether there are pending proxy tasks queued for writing.
fn has_pending_proxy_tasks(&self) -> bool {
false
}
/// Write queued proxy tasks in a cancel-safe manner.
///
/// # Panics
/// Panics if the Custom session does not implement the proxy task API.
fn write_proxy_tasks(&mut self) -> impl Future<Output = Result<bool>> + Send {
async { panic!("Custom proxy task API not implemented") }
}
fn set_read_timeout(&mut self, timeout: Option<Duration>);
fn get_read_timeout(&self) -> Option<Duration>;
fn set_write_timeout(&mut self, timeout: Option<Duration>);
fn get_write_timeout(&self) -> Option<Duration>;
fn set_total_drain_timeout(&mut self, timeout: Option<Duration>);
fn get_total_drain_timeout(&self) -> Option<Duration>;
fn request_summary(&self) -> String;
fn response_written(&self) -> Option<&ResponseHeader>;
View on GitHub (pinned to 4487f7b2ab)