Description
An OpenAPI 3.0 Link Object cannot specify both operationId and operationRef. These fields are mutually exclusive even when they point to the same operation.
Potential impact
- Specification validation may fail, or tools may handle the link inconsistently.
- API users may be unsure which operation the link identifies.
Remediation
Use operationId for a unique operation ID in the document, or operationRef for a URI pointing to an operation. Remove the other field, then validate the target and the parameters passed to it.
Examples
The first example identifies the same operation through both operationId and operationRef. References connecting this reusable response to an operation and the external schema documents are omitted.
Before
{
"openapi": "3.0.0",
"info": {
"title": "Simple API overview",
"version": "1.0.0"
},
"paths": {
"/": {
"get": {
"responses": {
"200": {
"description": "the user being returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"uuid": {
"type": "string",
"format": "uuid"
}
}
}
}
}
}
}
}
},
"/users/{userid}/address": {
"parameters": [
{
"name": "userid",
"in": "path",
"required": true,
"description": "the user identifier, as userId",
"schema": {
"type": "string"
}
}
],
"get": {
"operationId": "getUserAddress",
"responses": {
"200": {
"description": "the user's address"
}
}
}
}
},
"components": {
"responses": {
"200": {
"description": "the user being returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"uuid": {
"type": "string",
"format": "uuid"
}
}
}
}
},
"links": {
"address": {
"operationId": "getUserAddress",
"operationRef": "#/paths/~1users~1{userid}~1address/get",
"parameters": {
"userid": "$response.body#/uuid"
}
}
}
}
},
"schemas": {
"Pet": {
"$ref": "../models/pet.yaml"
},
"User": {
"$ref": "https://api.example.com/v2/openapi.yaml#/components/schemas/User"
}
}
}
}
After
{
"openapi": "3.0.0",
"info": {
"title": "Simple API overview",
"version": "1.0.0"
},
"paths": {
"/": {
"get": {
"responses": {
"200": {
"description": "the user being returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"uuid": {
"type": "string",
"format": "uuid"
}
}
}
}
}
}
}
}
},
"/users/{userid}/address": {
"parameters": [
{
"name": "userid",
"in": "path",
"required": true,
"description": "the user identifier, as userId",
"schema": {
"type": "string"
}
}
],
"get": {
"operationId": "getUserAddress",
"responses": {
"200": {
"description": "the user's address"
}
}
}
}
},
"components": {
"responses": {
"200": {
"description": "the user being returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"uuid": {
"type": "string",
"format": "uuid"
}
}
}
}
},
"links": {
"address": {
"operationId": "getUserAddress",
"parameters": {
"userid": "$response.body#/uuid"
}
}
}
}
},
"schemas": {
"Pet": {
"$ref": "../models/pet.yaml"
},
"User": {
"$ref": "https://api.example.com/v2/openapi.yaml#/components/schemas/User"
}
}
}
}
The second example keeps only operationId. It retains the mapping from the response’s uuid to the target userid parameter; the declaration does not automatically perform the subsequent call.