t8y2/dbx · error

ETCD_V2_UNSUPPORTED

ETCD_V2_UNSUPPORTED

Error message

ETCD_V2_UNSUPPORTED: %s is not available on the etcd v2 API

What it means

The v2 runtime's handle() dispatch checks isEtcd3OnlyMethod and rejects methods that exist only in etcd's v3 API (e.g. kv_history, etcd_compact, etcd_defrag, watches, leases) with this coded error. The v2 driver cannot emulate those primitives, so it fails explicitly instead of silently misbehaving.

Solutions

  1. Use the etcd3 (gRPC) driver for methods requiring v3 semantics.
  2. Gate the call behind a capability check (isEtcd3OnlyMethod or equivalent) before dispatch.
  3. Rework the operation using v2 primitives where possible (e.g. poll keys instead of watch).

Example fix

// before
handle(v2Session, "etcd_defrag", {})
// after
if !isEtcd3OnlyMethod("etcd_defrag") { handle(v2Session, "etcd_defrag", {}) } // or use the v3 driver
Defensive patterns

Strategy: validation

Validate before calling

if isEtcd3OnlyMethod(method) { return fmt.Errorf("method %s requires the etcd3 driver; v2 driver in use", method) }

Type guard

func v2Capable(method string) bool { return !isEtcd3OnlyMethod(method) }

Try / catch

if err := handle(session, method, params); err != nil && strings.Contains(err.Error(), "ETCD_V2_UNSUPPORTED") {
    return routeToV3Driver(session, method, params)
}

Prevention

When it happens

Trigger: Sending an etcd3-only method name (per isEtcd3OnlyMethod's switch list) to the etcd2 driver's handle(), e.g. calling etcd_compact or kv_history through the v2 agent.

Common situations: Shared client code written against an etcd3 driver pointed at the v2 driver, feature-detection missing, tests like TestEtcd3OnlyMethodsRejected exercising the rejection path.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/620475137e89e3f0. Report an issue: GitHub.

Appendix: source

Thrown at agents/drivers/etcd2-go/main.go:388

	case "etcd_auth_role_get":
		return s.authRoleGet(params)
	case "etcd_auth_role_add":
		return s.authRoleAdd(params)
	case "etcd_auth_role_delete":
		return s.authRoleDelete(params)
	case "etcd_auth_role_grant_permission":
		return s.authRolePermission(params, true)
	case "etcd_auth_role_revoke_permission":
		return s.authRolePermission(params, false)
	case "disconnect":
		s.close()
		return map[string]bool{"ok": true}, nil
	case "shutdown":
		s.close()
		return map[string]bool{"ok": true}, nil
	default:
		if isEtcd3OnlyMethod(method) {
			return nil, fmt.Errorf("ETCD_V2_UNSUPPORTED: %s is not available on the etcd v2 API", method)
		}
		return nil, fmt.Errorf("unknown method: %s", method)
	}
}

func isEtcd3OnlyMethod(method string) bool {
	switch method {
	case "kv_history", "etcd_compact", "etcd_defrag",
		"etcd_lease_list", "etcd_lease_get", "etcd_lease_grant",
		"etcd_lease_keepalive_once", "etcd_lease_revoke":
		return true
	}
	return false
}

func decodeParams(params map[string]json.RawMessage, target any) error {
	if params == nil {
		params = map[string]json.RawMessage{}

View on GitHub (pinned to c0390bff16)