AlexxIT/go2rtc · error
onvif: unsupported service
Error message
onvif: unsupported service
What it means
Request in pkg/onvif/client.go is the low-level SOAP dispatcher for all ONVIF service calls (GetProfile, GetVideoSourceConfiguration, GetStreamUri, GetSnapshotUri, GetServiceCapabilities, DeviceRequest). Each service method resolves a service URL (device/media/etc.); if the resolved URL is an empty string — meaning the device did not expose that service or the capability was never discovered — Request refuses to send and returns errors.New("onvif: unsupported service").
Solutions
- Check which services the camera exposes (DeviceService/GetServices or GetCapabilities) and only call operations for services that exist
- Verify discovery populated the service URLs — re-probe the device and confirm mediaURL/deviceURL are non-empty before use
- Use a camera/ONVIF-conformance tool (ONVIF Device Manager) to confirm whether the media service is implemented at all
- Upgrade the camera firmware or choose a device with full ONVIF Profile S support if you need streaming URIs
- Guard your code: call GetServiceCapabilities/GetStreamUri only when the corresponding URL field is non-empty
Example fix
// before
uri, err := cam.GetStreamUri(...) // panics-free but errors: unsupported service
// after
if cam.GetStreamUriURL() == "" { // media service not exposed
return nil, errors.New("camera does not support ONVIF media service")
}
uri, err := cam.GetStreamUri(...) Defensive patterns
Strategy: type-guard
Validate before calling
if c.mediaURL == "" {
return errors.New("device does not expose the ONVIF media service")
} Type guard
func supportsMedia(c *onvif.Client) bool { return c.MediaURL() != "" }
func supportsDevice(c *onvif.Client) bool { return c.DeviceURL() != "" } Try / catch
uri, err := cam.GetStreamUri(ctx, tok)
if err != nil {
if err.Error() == "onvif: unsupported service" {
return nil, fmt.Errorf("camera %s lacks ONVIF media service; streaming unavailable", camID)
}
return err
} Prevention
- Query GetServices/GetCapabilities before using stream/snapshot APIs and store which services exist
- Prefer cameras with ONVIF Profile S conformance for streaming features
- Re-run service discovery after firmware updates — URLs can appear/disappear
- Centralize ONVIF calls behind a capability-checking wrapper so empty service URLs are handled in one place
When it happens
Trigger: Calling any ONVIF operation when the corresponding service URL is empty: the camera does not implement the media service (no GetStreamUri/GetSnapshotUri/GetProfile), or the device/PTZ/etc. service was not found during capability discovery.
Common situations: Cheap/IP cameras that omit ONVIF media or analytics services entirely; a camera that reports services at discovery time but whose URLs weren't populated (device returned no XAddr for the service); calling snapshot/stream helpers against a doorbell or encoder that only implements device service.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/7e0ae9066e812aa0.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/onvif/client.go:179
switch operation {
case DeviceGetServices:
operation = `<tds:GetServices><tds:IncludeCapability>true</tds:IncludeCapability></tds:GetServices>`
case DeviceGetCapabilities:
operation = `<tds:GetCapabilities><tds:Category>All</tds:Category></tds:GetCapabilities>`
default:
operation = `<tds:` + operation + `/>`
}
return c.Request(c.deviceURL, operation)
}
func (c *Client) MediaRequest(operation string) ([]byte, error) {
operation = `<trt:` + operation + `/>`
return c.Request(c.mediaURL, operation)
}
func (c *Client) Request(url, body string) ([]byte, error) {
if url == "" {
return nil, errors.New("onvif: unsupported service")
}
e := NewEnvelopeWithUser(c.url.User)
e.Append(body)
client := &http.Client{Timeout: time.Second * 5000}
res, err := client.Post(url, `application/soap+xml;charset=utf-8`, bytes.NewReader(e.Bytes()))
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, errors.New("onvif: wrong response " + res.Status)
}
return io.ReadAll(res.Body)
}View on GitHub (pinned to c245815e75)