Skip to content

Commit ddbbed1

Browse files
authored
release: v2.10.1
2 parents 696b1e1 + c4d321d commit ddbbed1

24 files changed

+1566
-64
lines changed

.config/mise/config.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[tools]
22
# renovate-automation: rustc version
33
rust = "1.91.1"
4-
"aqua:cargo-bins/cargo-binstall" = "1.16.2"
4+
"aqua:cargo-bins/cargo-binstall" = "1.16.3"
55
"cargo:cargo-nextest" = "0.9.70"
6-
"cargo:cargo-deny" = "0.18.2"
6+
"cargo:cargo-deny" = "0.19.0"
77
"cargo:cargo-edit" = "0.13.0"
88
"cargo:cargo-about" = "0.8.0"
99
"cargo:cargo-insta" = "1.38.0"
@@ -13,7 +13,7 @@ rust = "1.91.1"
1313
"cargo:typos-cli" = "1.31.1"
1414
protoc = "33.1"
1515
gh = "2.72.0"
16-
helm = "3.19.2"
16+
helm = "3.19.4"
1717
helm-docs = "1.14.2"
1818
yq = "4.49.2"
1919
jq = "1.8.1"

.github/CODEOWNERS

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/docs/ @apollographql/docs
2-
/.changesets/ @apollographql/docs
31
/apollo-federation/ @apollographql/fed-core @apollographql/rust-platform
42
/apollo-federation/src/connectors @apollographql/graph-dev
53
/apollo-router/ @apollographql/router-core

CHANGELOG.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,66 @@
22

33
This project adheres to [Semantic Versioning v2.0.0](https://semver.org/spec/v2.0.0.html).
44

5+
# [2.10.1] - 2026-03-11
6+
7+
## 🐛 Fixes
8+
9+
### Enforce feature restrictions for warning-state licenses
10+
11+
The router now enforces license restrictions even when a license is in a warning state. Previously, warning-state licenses could bypass enforcement for restricted features.
12+
13+
If your deployment uses restricted features, the router returns an error instead of continuing to run.
14+
15+
By [@aaronArinder](https://github.com/aaronArinder) in https://github.com/apollographql/router/pull/8768
16+
17+
## 🧪 Experimental
18+
19+
### Add `experimental_hoist_orphan_errors` to control orphan error path assignment
20+
21+
The GraphQL specification requires that errors include a `path` pointing to the most specific field where the error occurred. When a subgraph returns entity errors without valid paths, the router's default behavior is its closest attempt at spec compliance: it assigns each error to every matching entity path in the response. This is the correct behavior when subgraphs respond correctly.
22+
23+
However, when a subgraph returns a large number of entity errors without valid paths — for example, 2000 errors for 2000 expected entities — this causes a multiplicative explosion in the error array that can lead to significant memory pressure and out-of-memory kills. The root cause is the subgraph: a spec-compliant subgraph includes correct paths on its entity errors, and fixing the subgraph is the right long-term solution.
24+
25+
The new `experimental_hoist_orphan_errors` configuration provides an important mitigation while you work toward that fix. When enabled, the router assigns each orphaned error to the nearest non-array ancestor path instead of duplicating it across every entity. This trades spec-precise path assignment for substantially reduced error volume in the response — a conscious trade-off, not a strict improvement.
26+
27+
To target a specific subgraph:
28+
29+
```yaml
30+
experimental_hoist_orphan_errors:
31+
subgraphs:
32+
my_subgraph:
33+
enabled: true
34+
```
35+
36+
To target all subgraphs:
37+
38+
```yaml
39+
experimental_hoist_orphan_errors:
40+
all:
41+
enabled: true
42+
```
43+
44+
To target all subgraphs except one:
45+
46+
```yaml
47+
experimental_hoist_orphan_errors:
48+
all:
49+
enabled: true
50+
subgraphs:
51+
noisy_one:
52+
enabled: false
53+
```
54+
55+
Per-subgraph settings override `all`. Note that this feature reduces the number of propagated errors but doesn't impose a hard cap — if your subgraph returns an extremely large number of errors, the router still processes all of them.
56+
57+
You'll likely know if you need this. Use it sparingly, and enable it only if you're affected and have been advised to do so. The behavior of this option is expected to change in a future release.
58+
59+
For full configuration reference and additional examples, see the [`experimental_hoist_orphan_errors` documentation](https://www.apollographql.com/docs/graphos/routing/configuration/yaml#experimental_hoist_orphan_errors).
60+
61+
By [@aaronArinder](https://github.com/aaronArinder) in https://github.com/apollographql/router/pull/8998
62+
63+
64+
565
# [2.10.0] - 2025-12-11
666

767
## 🚀 Features

Cargo.lock

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ dependencies = [
212212

213213
[[package]]
214214
name = "apollo-federation"
215-
version = "2.10.0"
215+
version = "2.10.1"
216216
dependencies = [
217217
"apollo-compiler",
218218
"apollo-federation",
@@ -286,7 +286,7 @@ dependencies = [
286286

287287
[[package]]
288288
name = "apollo-router"
289-
version = "2.10.0"
289+
version = "2.10.1"
290290
dependencies = [
291291
"addr2line 0.25.1",
292292
"ahash",
@@ -462,7 +462,7 @@ dependencies = [
462462

463463
[[package]]
464464
name = "apollo-router-benchmarks"
465-
version = "2.10.0"
465+
version = "2.10.1"
466466
dependencies = [
467467
"apollo-parser",
468468
"apollo-router",
@@ -1469,9 +1469,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
14691469

14701470
[[package]]
14711471
name = "bytes"
1472-
version = "1.11.0"
1472+
version = "1.11.1"
14731473
source = "registry+https://github.com/rust-lang/crates.io-index"
1474-
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
1474+
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
14751475
dependencies = [
14761476
"serde",
14771477
]
@@ -4095,9 +4095,9 @@ dependencies = [
40954095

40964096
[[package]]
40974097
name = "lru"
4098-
version = "0.16.2"
4098+
version = "0.16.3"
40994099
source = "registry+https://github.com/rust-lang/crates.io-index"
4100-
checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f"
4100+
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
41014101
dependencies = [
41024102
"hashbrown 0.16.1",
41034103
]
@@ -4425,9 +4425,9 @@ dependencies = [
44254425

44264426
[[package]]
44274427
name = "num-conv"
4428-
version = "0.1.0"
4428+
version = "0.2.0"
44294429
source = "registry+https://github.com/rust-lang/crates.io-index"
4430-
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
4430+
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
44314431

44324432
[[package]]
44334433
name = "num-integer"
@@ -6791,32 +6791,32 @@ dependencies = [
67916791

67926792
[[package]]
67936793
name = "time"
6794-
version = "0.3.44"
6794+
version = "0.3.47"
67956795
source = "registry+https://github.com/rust-lang/crates.io-index"
6796-
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
6796+
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
67976797
dependencies = [
67986798
"deranged",
67996799
"itoa",
68006800
"libc",
68016801
"num-conv",
68026802
"num_threads",
68036803
"powerfmt",
6804-
"serde",
6804+
"serde_core",
68056805
"time-core",
68066806
"time-macros",
68076807
]
68086808

68096809
[[package]]
68106810
name = "time-core"
6811-
version = "0.1.6"
6811+
version = "0.1.8"
68126812
source = "registry+https://github.com/rust-lang/crates.io-index"
6813-
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
6813+
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
68146814

68156815
[[package]]
68166816
name = "time-macros"
6817-
version = "0.2.24"
6817+
version = "0.2.27"
68186818
source = "registry+https://github.com/rust-lang/crates.io-index"
6819-
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
6819+
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
68206820
dependencies = [
68216821
"num-conv",
68226822
"time-core",

apollo-federation/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "apollo-federation"
3-
version = "2.10.0"
3+
version = "2.10.1"
44
authors = ["The Apollo GraphQL Contributors"]
55
edition = "2024"
66
description = "Apollo Federation"

apollo-router-benchmarks/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "apollo-router-benchmarks"
3-
version = "2.10.0"
3+
version = "2.10.1"
44
authors = ["Apollo Graph, Inc. <packages@apollographql.com>"]
55
edition = "2021"
66
license = "Elastic-2.0"

apollo-router/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "apollo-router"
3-
version = "2.10.0"
3+
version = "2.10.1"
44
authors = ["Apollo Graph, Inc. <packages@apollographql.com>"]
55
repository = "https://github.com/apollographql/router/"
66
documentation = "https://docs.rs/apollo-router"
@@ -59,7 +59,7 @@ snapshot = []
5959
addr2line = "0.25.0"
6060
anyhow = "1.0.86"
6161
apollo-compiler.workspace = true
62-
apollo-federation = { path = "../apollo-federation", version = "=2.10.0" }
62+
apollo-federation = { path = "../apollo-federation", version = "=2.10.1" }
6363
async-compression = { version = "0.4.6", features = [
6464
"tokio",
6565
"futures-io",

apollo-router/src/configuration/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ pub struct Configuration {
222222
/// Type conditioned fetching configuration.
223223
#[serde(default)]
224224
pub(crate) experimental_type_conditioned_fetching: bool,
225+
226+
/// When enabled for specific subgraphs, orphan errors (those without a valid
227+
/// `_entities` path) are assigned to the nearest non-array ancestor in the
228+
/// response path, preventing them from being duplicated across every array
229+
/// element.
230+
#[serde(default)]
231+
pub(crate) experimental_hoist_orphan_errors: SubgraphConfiguration<HoistOrphanErrors>,
225232
}
226233

227234
impl PartialEq for Configuration {
@@ -256,6 +263,7 @@ impl<'de> serde::Deserialize<'de> for Configuration {
256263
experimental_chaos: chaos::Config,
257264
batching: Batching,
258265
experimental_type_conditioned_fetching: bool,
266+
experimental_hoist_orphan_errors: SubgraphConfiguration<HoistOrphanErrors>,
259267
}
260268
let mut ad_hoc: AdHocConfiguration = serde::Deserialize::deserialize(deserializer)?;
261269

@@ -287,6 +295,7 @@ impl<'de> serde::Deserialize<'de> for Configuration {
287295
limits: ad_hoc.limits,
288296
experimental_chaos: ad_hoc.experimental_chaos,
289297
experimental_type_conditioned_fetching: ad_hoc.experimental_type_conditioned_fetching,
298+
experimental_hoist_orphan_errors: ad_hoc.experimental_hoist_orphan_errors,
290299
plugins: ad_hoc.plugins,
291300
apollo_plugins: ad_hoc.apollo_plugins,
292301
batching: ad_hoc.batching,
@@ -327,6 +336,7 @@ impl Configuration {
327336
chaos: Option<chaos::Config>,
328337
uplink: Option<UplinkConfig>,
329338
experimental_type_conditioned_fetching: Option<bool>,
339+
experimental_hoist_orphan_errors: Option<SubgraphConfiguration<HoistOrphanErrors>>,
330340
batching: Option<Batching>,
331341
server: Option<Server>,
332342
) -> Result<Self, ConfigurationError> {
@@ -356,6 +366,7 @@ impl Configuration {
356366
batching: batching.unwrap_or_default(),
357367
experimental_type_conditioned_fetching: experimental_type_conditioned_fetching
358368
.unwrap_or_default(),
369+
experimental_hoist_orphan_errors: experimental_hoist_orphan_errors.unwrap_or_default(),
359370
notify,
360371
};
361372

@@ -500,6 +511,7 @@ impl Configuration {
500511
uplink,
501512
experimental_type_conditioned_fetching: experimental_type_conditioned_fetching
502513
.unwrap_or_default(),
514+
experimental_hoist_orphan_errors: Default::default(),
503515
batching: batching.unwrap_or_default(),
504516
raw_yaml: None,
505517
};
@@ -1574,3 +1586,17 @@ impl Batching {
15741586
}
15751587
}
15761588
}
1589+
1590+
/// Per-subgraph configuration for hoisting orphan errors.
1591+
///
1592+
/// "Orphan errors" are errors from entity fetches that lack a valid `_entities` path.
1593+
/// When hoisting is enabled, these errors are assigned to the nearest non-array
1594+
/// ancestor in the response path, preventing them from being duplicated across
1595+
/// every element in an array.
1596+
#[derive(Debug, Clone, Default, Deserialize, Serialize, JsonSchema)]
1597+
#[serde(deny_unknown_fields)]
1598+
pub(crate) struct HoistOrphanErrors {
1599+
/// Enable hoisting of orphan errors for this subgraph.
1600+
#[serde(default)]
1601+
pub(crate) enabled: bool,
1602+
}

apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5481,6 +5481,18 @@ expression: "&schema"
54815481
}
54825482
]
54835483
},
5484+
"HoistOrphanErrors": {
5485+
"additionalProperties": false,
5486+
"description": "Per-subgraph configuration for hoisting orphan errors.\n\n\"Orphan errors\" are errors from entity fetches that lack a valid `_entities` path.\nWhen hoisting is enabled, these errors are assigned to the nearest non-array\nancestor in the response path, preventing them from being duplicated across\nevery element in an array.",
5487+
"properties": {
5488+
"enabled": {
5489+
"default": false,
5490+
"description": "Enable hoisting of orphan errors for this subgraph.",
5491+
"type": "boolean"
5492+
}
5493+
},
5494+
"type": "object"
5495+
},
54845496
"Homepage": {
54855497
"additionalProperties": false,
54865498
"description": "Configuration options pertaining to the home page.",
@@ -9476,6 +9488,31 @@ expression: "&schema"
94769488
},
94779489
"type": "object"
94789490
},
9491+
"SubgraphHoistOrphanErrorsConfiguration": {
9492+
"description": "Configuration options pertaining to the subgraph server component.",
9493+
"properties": {
9494+
"all": {
9495+
"allOf": [
9496+
{
9497+
"$ref": "#/definitions/HoistOrphanErrors"
9498+
}
9499+
],
9500+
"default": {
9501+
"enabled": false
9502+
},
9503+
"description": "options applying to all subgraphs"
9504+
},
9505+
"subgraphs": {
9506+
"additionalProperties": {
9507+
"$ref": "#/definitions/HoistOrphanErrors"
9508+
},
9509+
"default": {},
9510+
"description": "per subgraph options",
9511+
"type": "object"
9512+
}
9513+
},
9514+
"type": "object"
9515+
},
94799516
"SubgraphInvalidationConfig": {
94809517
"additionalProperties": false,
94819518
"properties": {
@@ -12065,6 +12102,20 @@ expression: "&schema"
1206512102
"experimental_diagnostics": {
1206612103
"$ref": "#/definitions/Config5"
1206712104
},
12105+
"experimental_hoist_orphan_errors": {
12106+
"allOf": [
12107+
{
12108+
"$ref": "#/definitions/SubgraphHoistOrphanErrorsConfiguration"
12109+
}
12110+
],
12111+
"default": {
12112+
"all": {
12113+
"enabled": false
12114+
},
12115+
"subgraphs": {}
12116+
},
12117+
"description": "When enabled for specific subgraphs, orphan errors (those without a valid\n`_entities` path) are assigned to the nearest non-array ancestor in the\nresponse path, preventing them from being duplicated across every array\nelement."
12118+
},
1206812119
"experimental_type_conditioned_fetching": {
1206912120
"default": false,
1207012121
"description": "Type conditioned fetching configuration.",

0 commit comments

Comments
 (0)