IPIP-0513: Routing V1 Returns 200 for Empty Results

Related Issues
ipfs/boxo/issues/1024
ipfs/specs/pull/337
History
Commit History
Feedback
GitHub ipfs/specs (inspect source, open issue)

1. Summary

Change the Delegated Routing V1 HTTP API server recommendation to return HTTP status code 200 (OK) with empty results instead of 404 (Not Found) when no matching records are found. This improves semantic correctness, prevents unwanted browser console errors, and provides separate guidance for servers and clients - with clients required to handle both response codes for backward compatibility, resiliency, and maximal interoperability with different server implementations.

2. Motivation

The current Delegated Routing V1 HTTP API specification requires servers to return HTTP 404 (Not Found) when no matching records are found for a query. This creates two significant problems:

  1. Browser Console Errors: When routing queries are made from web browsers and return 404s, browsers log error messages to the console that cannot be prevented programmatically. These error messages confuse users who understandably think something is broken, when in reality an empty result is a normal and expected outcome for many queries.

  2. Semantic Incorrectness: HTTP 404 means "the requested resource does not exist." However, when querying /routing/v1/providers/{cid}, the endpoint itself exists and is functioning correctly - it simply found no results. Returning 404 conflates "endpoint not found" with "no data found," which are fundamentally different conditions.

As noted in the original issue, querying for a peer with specific filters demonstrates this problem clearly:

3. Detailed design

3.1 Specification Changes

Update the following sections in specs/src/routing/http-routing-v1.md:

3.1.1 GET /routing/v1/providers/{cid}

Current:

  • 404 (Not Found): must be returned if no matching records are found.

New:

  • 200 (OK): the response body contains 0 or more records.
  • 404 (Not Found): SHOULD NOT be returned by a server, but if a client receives it, it MUST be interpreted as 200 with 0 results for backward compatibility, resiliency, and maximal interoperability.

3.1.2 GET /routing/v1/peers/{peer-id}

Current:

  • 404 (Not Found): must be returned if no matching records are found.

New:

  • 200 (OK): the response body contains 0 or more peer records.
  • 404 (Not Found): SHOULD NOT be returned by a server, but if a client receives it, it MUST be interpreted as 200 with 0 results for backward compatibility, resiliency, and maximal interoperability.

3.1.3 GET /routing/v1/ipns/{name}

Current:

  • 404 (Not Found): must be returned if no matching records are found.

New:

  • 200 (OK): the response body contains the IPNS Record for the given IPNS Name with Content-Type: application/vnd.ipfs.ipns-record. Any other content type MUST be interpreted as "no record found".
  • 404 (Not Found): SHOULD NOT be returned by a server, but if a client receives it, it MUST be interpreted as "no record found" for backward compatibility, resiliency, and maximal interoperability.

3.2 Response Format for Empty Results

For JSON responses (application/json):

{
  "Providers": []
}

or

{
  "Peers": []
}

For NDJSON streaming responses (application/x-ndjson):

For IPNS responses:

3.3 Cache Control

The existing cache control behavior remains unchanged:

4. Design rationale

4.1 Industry Best Practices

Research into REST API design patterns shows that returning 200 with empty collections is a common and recommended practice:

4.2 Semantic Correctness

The HTTP specification defines status codes with specific meanings:

For collection/search endpoints, the resource is the endpoint itself, not the data it returns. The endpoint exists and functions correctly regardless of whether it finds matching data.

4.3 User benefit

  1. Cleaner Browser Console: Web applications will no longer show error messages for normal operations, reducing user confusion and support requests.

  2. Clearer Semantics: Developers can distinguish between "endpoint doesn't exist" (real 404) and "no results found" (200 with empty data).

  3. Consistent Response Structure: Clients can use the same parsing logic whether results are empty or populated.

4.4 Compatibility

4.4.1 Backward Compatibility

This change is designed to be fully backward compatible by following the robustness principle - "be conservative in what you send, and liberal in what you accept":

  1. Servers (conservative sending): SHOULD return 200 for empty results
  2. Clients (liberal accepting): MUST handle both:
    • 200 with empty result list (new behavior)
    • 404 (old behavior from legacy servers)

This approach ensures maximum interoperability across the ecosystem while gradually transitioning to the semantically correct behavior.

4.4.2 Migration Strategy

  1. Phase 1: Update server implementations to return 200

    • Can be deployed immediately without breaking existing clients that handle both codes
  2. Phase 2: Update clients to have regression tests that ensure they handle both 200 and 404

    • Clients must maintain 404 handling indefinitely for compatibility with servers that return 404s

4.5 Security

This change has no security implications. It only affects the HTTP status code returned for empty results, not the data format, authentication, or authorization mechanisms.

4.6 Alternatives

4.6.1 204 No Content

Considered but rejected for the following reasons:

  1. Cannot include response body: 204 explicitly means no response body, but for JSON endpoints we want to maintain consistent response structure (empty arrays/objects)
  2. Incompatible with streaming: NDJSON streams need to specify content type and may include headers even when empty
  3. Inconsistent client handling: Different code paths needed for empty vs non-empty responses
  4. Limited applicability: Could only work for IPNS binary responses, not JSON/NDJSON

4.6.2 Keep 404 Status

Rejected because:

  1. Browser errors: The primary motivation is eliminating confusing console errors
  2. Semantic incorrectness: 404 should mean "endpoint not found" not "no data"
  3. Inconsistent with filters: Same endpoint returning different status codes based on filter parameters is confusing

4.6.3 API Versioning (/routing/v1.1)

Rejected in favor of in-place upgrade because:

  1. Backward compatible: The change can be made without breaking existing clients
  2. Simpler deployment: No need to maintain multiple API versions
  3. Faster adoption: No need for clients to explicitly opt into new version

5. Test fixtures

Implementations should test the following scenarios:

5.1 Empty Results Tests

  1. Providers endpoint with no results:

    GET /routing/v1/providers/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
    Response: 200 OK
    Body: {"Providers": []}
    
  2. NDJSON streaming with no results:

    GET /routing/v1/providers/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
    Accept: application/x-ndjson
    Response: 200 OK
    Content-Type: application/x-ndjson
    Body: [empty - no lines]
    
  3. IPNS endpoint with no results:

    GET /routing/v1/ipns/k51qzi5uqu5dhlbegona8wfyei6jnjuhrulz3t8femxtfmak9134qpqncw3poc
    Accept: application/vnd.ipfs.ipns-record
    Response: 200 OK
    Content-Type: text/plain; charset=utf-8
    Body: delegate error: routing: not found
    

    Note: The Content-Type is NOT application/vnd.ipfs.ipns-record, which indicates no record was found and parsing of body can be skipped. In this example, the body contains text/plain error message for convenience.

5.2 Backward Compatibility Tests

Clients MUST correctly handle:

  1. Old server returning 404 → treat as empty results
  2. New server returning 200 with empty body → treat as empty results
  3. New server returning 200 with results → process normally

A. References

[rfc2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119

B. Acknowledgments

We gratefully acknowledge the following individuals for their valuable contributions, ranging from minor suggestions to major insights, which have shaped and improved this specification.

Editor
Marcin Rataj (Shipyard) GitHub
Special Thanks
Alex Potsides (Shipyard) GitHub