Summary
POST /api/admin/strategies accepts a custom strategy type whose parameters list contains two parameters with the same name, stores both, and serves both back. The documentation states the opposite invariant, and since the parameter name is the key an SDK implementation reads the value by, one of the two is permanently unreachable.
Tested on main at 3d91a51635859f13a5bc23547d323c19718629fb (unleash-server 8.1.0), OSS, stock config, Postgres 15, admin token.
What the docs say
https://docs.getunleash.io/concepts/custom-activation-strategies, "Parameters":
Each parameter consists of three parts:
- a name: must be unique among the strategy's parameters.
Reproduction
Control first — a well-formed custom strategy is accepted and read back unchanged:
POST /api/admin/strategies
body: {"name":"tcrefQuota","parameters":[
{"name":"quota","type":"number","description":"how many are allowed","required":true},
{"name":"label","type":"string","description":"","required":false}]}
-> 201
{"displayName":null,"name":"tcrefQuota","editable":true,"description":null,
"parameters":[{"name":"quota","type":"number","description":"how many are allowed","required":true},
{"name":"label","type":"string","description":"","required":false}],
"deprecated":false,"title":null}
GET /api/admin/strategies/tcrefQuota
-> 200 (same document)
Now the same request with two parameters both named twin:
POST /api/admin/strategies
body: {"name":"tcrefTwins","parameters":[
{"name":"twin","type":"string","description":"","required":false},
{"name":"twin","type":"number","description":"","required":false}]}
-> 201
{"displayName":null,"name":"tcrefTwins","editable":true,"description":null,
"parameters":[{"name":"twin","type":"string","description":"","required":false},
{"name":"twin","type":"number","description":"","required":false}],
"deprecated":false,"title":null}
It is not just echoed back. Repeating the same thing by hand on a clean instance, this time with the two parameters described as first and second so they are distinguishable, they are persisted and served from the registry:
POST /api/admin/strategies {"name":"tcrefTwins","description":"two params, same name","parameters":[
{"name":"twin","type":"string","description":"first","required":false},
{"name":"twin","type":"number","description":"second","required":false}]}
-> 201
GET /api/admin/strategies/tcrefTwins
-> "parameters":[{"name":"twin","type":"string","description":"first","required":false},
{"name":"twin","type":"number","description":"second","required":false}]
select parameters from strategies where name = 'tcrefTwins';
-> [{"name":"twin","type":"string","description":"first","required":false},
{"name":"twin","type":"number","description":"second","required":false}]
Control — the sibling rule is enforced
The same server, on the same kind of duplicate-name mistake one level over, refuses with a 400. Creating strategy variants with two entries named twin:
-> 400
{"name":"BadDataError",
"message":"Request validation failed: your request body or params contain invalid data. Refer to the `details` list for more information.",
"details":[{"message":"\"[1]\" contains a duplicate value. You provided {\"name\":\"twin\",\"weight\":500,\"weightType\":\"variable\",\"stickiness\":\"default\"}."}]}
So the uniqueness policy exists in the product; strategy-type parameters just aren't covered by it. That is what makes this look like a gap rather than a deliberate decision.
Where
src/lib/openapi/spec/create-strategy-schema.ts declares parameters as a plain array with required: ['name', 'type'] per item and no uniqueness constraint, and nothing downstream checks for repeats.
Suggested fix
Reject duplicate parameter names on create and update of a strategy type (the variant path already has the error shape for it), or, at minimum, add uniqueItems-style validation on the parameter name in createStrategySchema / updateStrategySchema.
Suggested labels: bug
Summary
POST /api/admin/strategiesaccepts a custom strategy type whoseparameterslist contains two parameters with the samename, stores both, and serves both back. The documentation states the opposite invariant, and since the parameter name is the key an SDK implementation reads the value by, one of the two is permanently unreachable.Tested on
mainat3d91a51635859f13a5bc23547d323c19718629fb(unleash-server 8.1.0), OSS, stock config, Postgres 15, admin token.What the docs say
https://docs.getunleash.io/concepts/custom-activation-strategies, "Parameters":
Reproduction
Control first — a well-formed custom strategy is accepted and read back unchanged:
Now the same request with two parameters both named
twin:It is not just echoed back. Repeating the same thing by hand on a clean instance, this time with the two parameters described as
firstandsecondso they are distinguishable, they are persisted and served from the registry:Control — the sibling rule is enforced
The same server, on the same kind of duplicate-name mistake one level over, refuses with a 400. Creating strategy variants with two entries named
twin:So the uniqueness policy exists in the product; strategy-type parameters just aren't covered by it. That is what makes this look like a gap rather than a deliberate decision.
Where
src/lib/openapi/spec/create-strategy-schema.tsdeclaresparametersas a plain array withrequired: ['name', 'type']per item and no uniqueness constraint, and nothing downstream checks for repeats.Suggested fix
Reject duplicate parameter names on create and update of a strategy type (the variant path already has the error shape for it), or, at minimum, add
uniqueItems-style validation on the parameter name increateStrategySchema/updateStrategySchema.Suggested labels:
bug