Skip to content

Commit 291a0fc

Browse files
committed
IPIP-0513: Delegated Routing V1 returns 200 for empty results
Changes HTTP Delegated Routing V1 API to improve empty result handling: - servers SHOULD return 200 instead of 404 for empty results - clients MUST handle both 200 and 404 for backward compatibility - improves semantic correctness (404 means "endpoint not found", not "no data") - prevents browser console errors that confuse users Follows robustness principle for maximum interoperability. Related to ipfs/boxo#1024
1 parent 3dd66f5 commit 291a0fc

File tree

2 files changed

+226
-5
lines changed

2 files changed

+226
-5
lines changed

src/ipips/ipip-0513.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
---
2+
title: "IPIP-0513: Delegated Routing V1 HTTP API Returns 200 for Empty Results"
3+
date: 2025-09-11
4+
ipip: proposal
5+
editors:
6+
- name: Marcin Rataj
7+
github: lidel
8+
url: https://lidel.org/
9+
affiliation:
10+
name: Shipyard
11+
url: https://ipshipyard.com
12+
thanks:
13+
- name: Alex Potsides
14+
github: achingbrain
15+
affiliation:
16+
name: Shipyard
17+
url: https://ipshipyard.com
18+
relatedIssues:
19+
- https://github.com/ipfs/boxo/issues/1024
20+
- https://github.com/ipfs/specs/pull/337
21+
order: 513
22+
tags: ['ipips']
23+
---
24+
25+
## Summary
26+
27+
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.
28+
29+
## Motivation
30+
31+
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:
32+
33+
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.
34+
35+
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.
36+
37+
As noted in the original issue, querying for a peer with specific filters demonstrates this problem clearly:
38+
- A peer query without filters returns 200 with results
39+
- The same peer query with filters that match nothing returns 404
40+
- This suggests the endpoint suddenly "doesn't exist" when it clearly does
41+
42+
## Detailed design
43+
44+
### Specification Changes
45+
46+
Update the following sections in `specs/src/routing/http-routing-v1.md`:
47+
48+
#### GET /routing/v1/providers/{cid}
49+
Current:
50+
- `404` (Not Found): must be returned if no matching records are found.
51+
52+
New:
53+
- `200` (OK): the response body contains 0 or more records.
54+
- `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.
55+
56+
#### GET /routing/v1/peers/{peer-id}
57+
Current:
58+
- `404` (Not Found): must be returned if no matching records are found.
59+
60+
New:
61+
- `200` (OK): the response body contains 0 or more peer records.
62+
- `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.
63+
64+
#### GET /routing/v1/ipns/{name}
65+
Current:
66+
- `404` (Not Found): must be returned if no matching records are found.
67+
68+
New:
69+
- `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".
70+
- `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.
71+
72+
### Response Format for Empty Results
73+
74+
For JSON responses (`application/json`):
75+
```json
76+
{
77+
"Providers": []
78+
}
79+
```
80+
or
81+
```json
82+
{
83+
"Peers": []
84+
}
85+
```
86+
87+
For NDJSON streaming responses (`application/x-ndjson`):
88+
- Return 200 with an empty stream (zero lines)
89+
- Do not return any data lines
90+
91+
For IPNS responses:
92+
- Only `Content-Type: application/vnd.ipfs.ipns-record` indicates a valid IPNS record was found
93+
- Any other content type (e.g., `text/plain` with error message) MUST be interpreted as "no record found"
94+
- This allows servers to return 200 with different content types for error conditions while maintaining backward compatibility
95+
96+
### Cache Control
97+
98+
The existing cache control behavior remains unchanged:
99+
- Empty results should use shorter TTL (typically 15 seconds)
100+
- Non-empty results can use longer TTL based on the data
101+
102+
## Design rationale
103+
104+
### Industry Best Practices
105+
106+
Research into REST API design patterns shows that returning 200 with empty collections is a common and recommended practice:
107+
- Major APIs (GitHub, Google, AWS) follow this pattern for collection endpoints
108+
- REST principles suggest that a successful query with no results is still a success
109+
- The collection resource exists even when empty
110+
111+
### Semantic Correctness
112+
113+
The HTTP specification defines status codes with specific meanings:
114+
- **200 OK**: The request succeeded, here are the results (which may be empty)
115+
- **404 Not Found**: The requested resource (endpoint) does not exist
116+
- **204 No Content**: Success with explicitly no response body
117+
118+
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.
119+
120+
### User benefit
121+
122+
1. **Cleaner Browser Console**: Web applications will no longer show error messages for normal operations, reducing user confusion and support requests.
123+
124+
2. **Clearer Semantics**: Developers can distinguish between "endpoint doesn't exist" (real 404) and "no results found" (200 with empty data).
125+
126+
3. **Consistent Response Structure**: Clients can use the same parsing logic whether results are empty or populated.
127+
128+
### Compatibility
129+
130+
#### Backward Compatibility
131+
132+
This change is designed to be fully backward compatible by following the [robustness principle](https://specs.ipfs.tech/architecture/principles/#robustness) - "be conservative in what you send, and liberal in what you accept":
133+
134+
1. **Servers** (conservative sending): SHOULD return 200 for empty results
135+
2. **Clients** (liberal accepting): MUST handle both:
136+
- 200 with empty result list (new behavior)
137+
- 404 (old behavior from legacy servers)
138+
139+
This approach ensures maximum interoperability across the ecosystem while gradually transitioning to the semantically correct behavior.
140+
141+
#### Migration Strategy
142+
143+
1. **Phase 1**: Update server implementations to return 200
144+
- Can be deployed immediately without breaking existing clients that handle both codes
145+
146+
2. **Phase 2**: Update clients to have regression tests that ensure they handle both 200 and 404
147+
- Clients must maintain 404 handling indefinitely for compatibility with servers that return 404s
148+
149+
### Security
150+
151+
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.
152+
153+
### Alternatives
154+
155+
#### 204 No Content
156+
157+
**Considered but rejected** for the following reasons:
158+
159+
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)
160+
2. **Incompatible with streaming**: NDJSON streams need to specify content type and may include headers even when empty
161+
3. **Inconsistent client handling**: Different code paths needed for empty vs non-empty responses
162+
4. **Limited applicability**: Could only work for IPNS binary responses, not JSON/NDJSON
163+
164+
#### Keep 404 Status
165+
166+
**Rejected** because:
167+
168+
1. **Browser errors**: The primary motivation is eliminating confusing console errors
169+
2. **Semantic incorrectness**: 404 should mean "endpoint not found" not "no data"
170+
3. **Inconsistent with filters**: Same endpoint returning different status codes based on filter parameters is confusing
171+
172+
#### API Versioning (/routing/v1.1)
173+
174+
**Rejected** in favor of in-place upgrade because:
175+
176+
1. **Backward compatible**: The change can be made without breaking existing clients
177+
2. **Simpler deployment**: No need to maintain multiple API versions
178+
3. **Faster adoption**: No need for clients to explicitly opt into new version
179+
180+
## Test fixtures
181+
182+
Implementations should test the following scenarios:
183+
184+
### Empty Results Tests
185+
186+
1. **Providers endpoint with no results**:
187+
```
188+
GET /routing/v1/providers/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
189+
Response: 200 OK
190+
Body: {"Providers": []}
191+
```
192+
193+
2. **NDJSON streaming with no results**:
194+
```
195+
GET /routing/v1/providers/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
196+
Accept: application/x-ndjson
197+
Response: 200 OK
198+
Content-Type: application/x-ndjson
199+
Body: [empty - no lines]
200+
```
201+
202+
3. **IPNS endpoint with no results**:
203+
```
204+
GET /routing/v1/ipns/k51qzi5uqu5dhlbegona8wfyei6jnjuhrulz3t8femxtfmak9134qpqncw3poc
205+
Accept: application/vnd.ipfs.ipns-record
206+
Response: 200 OK
207+
Content-Type: text/plain; charset=utf-8
208+
Body: delegate error: routing: not found
209+
```
210+
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.
211+
212+
### Backward Compatibility Tests
213+
214+
Clients MUST correctly handle:
215+
1. Old server returning 404 → treat as empty results
216+
2. New server returning 200 with empty body → treat as empty results
217+
3. New server returning 200 with results → process normally
218+
219+
### Copyright
220+
221+
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

src/routing/http-routing-v1.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Optional `?filter-protocols` to apply IPFS Protocol Filtering from [IPIP-484](ht
105105
#### Response Status Codes
106106

107107
- `200` (OK): the response body contains 0 or more records.
108-
- `404` (Not Found): must be returned if no matching records are found.
108+
- `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.
109109
- `422` (Unprocessable Entity): request does not conform to schema or semantic constraints.
110110

111111
#### Response Headers
@@ -162,8 +162,8 @@ Optional, same rules as [`filter-protocols` providers request query parameter](#
162162

163163
#### Response Status Codes
164164

165-
- `200` (OK): the response body contains the peer record.
166-
- `404` (Not Found): must be returned if no matching records are found.
165+
- `200` (OK): the response body contains 0 or more peer records.
166+
- `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.
167167
- `422` (Unprocessable Entity): request does not conform to schema or semantic constraints.
168168

169169
#### Response Headers
@@ -210,8 +210,8 @@ Each object in the `Peers` list is a record conforming to the [Peer Schema](#pee
210210

211211
#### Response Status Codes
212212

213-
- `200` (OK): the response body contains the :ref[IPNS Record] for the given :ref[IPNS Name].
214-
- `404` (Not Found): must be returned if no matching records are found.
213+
- `200` (OK): the response body contains the :ref[IPNS Record] for the given :ref[IPNS Name] with `Content-Type: application/vnd.ipfs.ipns-record`. Any other content type MUST be interpreted as "no record found".
214+
- `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.
215215
- `406` (Not Acceptable): requested content type is missing or not supported. Error message returned in body should inform the user to retry with `Accept: application/vnd.ipfs.ipns-record`.
216216

217217
#### Response Headers

0 commit comments

Comments
 (0)