siyuan-note/siyuan · error

unregistered API contract

Error message

unregistered API contract: %s %s

What it means

ValidateRawSSEEvent iterates the bundle's registered endpoints and, when no endpoint matches the given method and path, reports the pair as an unregistered API contract. This is the not-found path of the raw SSE validation lookup and echoes the method and path that failed to match.

Solutions

  1. Register the endpoint in the contract and rebuild the bundle
  2. Correct the method and path strings to the registered values (check exact spelling and slashes)
  3. List b.Endpoints or the contract definition to confirm the route exists
  4. Update stale test/client code after a route rename

Example fix

// before
bundle.ValidateRawSSEEvent("POST", "/api/blocks/raw-sse", ...)
// after
bundle.ValidateRawSSEEvent("GET", "/api/blocks/raw-sse", ...)
Defensive patterns

Strategy: validation

Validate before calling

const registered = bundle.Endpoints.some(e => e.Method === method && e.Path === path);
if (!registered) throw new Error("route not in bundle: " + method + " " + path);

Try / catch

try { bundle.ValidateRawSSEEvent(method, path, ...); } catch (e) { if (String(e).startsWith("unregistered API contract")) { await rebuildBundle(); retry once; } else throw e; }

Prevention

When it happens

Trigger: Calling bundle.ValidateRawSSEEvent with a method or path that is not in b.Endpoints — misspelled path, wrong HTTP method (POST vs GET), or the endpoint was never added to the bundle.

Common situations: Path typos or missing trailing/leading slash; endpoint renamed or removed in a contract update while callers kept the old route; tests written before the endpoint was registered; forgetting BuildBundle registration.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/6f1941aefda3727c. Report an issue: GitHub.

Appendix: source

Thrown at kernel/apicontract/broadcast_protocol.go:76

	for _, allowed := range schema.Raw.UpgradeErrorStatuses {
		if status == allowed && media == "text/plain" {
			return true, nil
		}
	}
	return false, nil
}

// ValidateRawSSEEvent 校验事件协议;事件数据是原始字节,不进行 JSON 解码或 Base64 转码。
func (b *Bundle) ValidateRawSSEEvent(method, path, name, id string, retry uint, payload []byte) error {
	for _, endpoint := range b.Endpoints {
		if endpoint.Method == method && endpoint.Path == path {
			if endpoint.SSE == nil || endpoint.SSE.Raw == nil {
				return fmt.Errorf("endpoint does not declare raw SSE events")
			}
			return nil
		}
	}
	return fmt.Errorf("unregistered API contract: %s %s", method, path)
}

func (b *Bundle) ValidateRawWebSocketFrame(method, path string, incoming bool, frameType int, payload []byte) error {
	for _, endpoint := range b.Endpoints {
		if endpoint.Method == method && endpoint.Path == path {
			if endpoint.WebSocket == nil || endpoint.WebSocket.Raw == nil {
				return fmt.Errorf("endpoint does not declare raw WebSocket frames")
			}
			for _, allowed := range endpoint.WebSocket.Raw.Frames {
				if frameType == allowed {
					if frameType >= 8 && len(payload) > 125 {
						return fmt.Errorf("WebSocket control frame exceeds 125 bytes")
					}
					return nil
				}
			}
			return fmt.Errorf("undeclared raw WebSocket frame: %d", frameType)
		}

View on GitHub (pinned to 9f775e8a12)