I'm encountering a Spring Boot startup failure during Maven build tests in my recommendation microservice. The error indicates an invalid mapping pattern /recommendation}, but I've verified all my endpoint mappings and found no typos with extra braces.
Error Message:
org.springframework.web.util.pattern.PatternParseException:
Missing preceding open capture character before variable name{
...
Invalid mapping pattern detected: /recommendation}
^
RecommendationService interface:
public interface RecommendationService {
@PostMapping(
value = "/recommendation",
consumes = "application/json",
produces = "application/json")
Recommendation createRecommendation(@RequestBody Recommendation body);
@GetMapping(
value = "/recommendation",
produces = "application/json")
List getRecommendations(@RequestParam(value = "productId") int productId);
@DeleteMapping(value = "/recommendation")
void deleteRecommendation(@RequestParam(value = "productId") int productId);
}
RecommendationServiceImpl class:
@RestController
public class RecommendationServiceImpl implements RecommendationService {
private final ServiceUtil serviceUtil;
private final RecommendationRepository repository;
private final RecommendationMapper mapper;
public RecommendationServiceImpl(ServiceUtil serviceUtil, RecommendationRepository repository, @Qualifier("recommendationMapperImpl") RecommendationMapper mapper) {
this.serviceUtil = serviceUtil;
this.repository = repository;
this.mapper = mapper;
}
@Override
public Recommendation createRecommendation(Recommendation body) {
try {
RecommendationEntity entity = mapper.apiToEntity(body);
RecommendationEntity newEntity = repository.save(entity);
return mapper.entityToApi(newEntity);
} catch (DuplicateKeyException e) {
throw new InvalidInputException("Duplicate key, Product Id: " + body.getProductId() + ", Recommendation Id:" + body.getRecommendationId());
}
}
@Override
public List getRecommendations(int productId) {
if (productId < 1) {
throw new InvalidInputException("Invalid productId: " + productId);
}
List entityList = repository.findByProductId(productId);
List list = mapper.entityListToApiList(entityList);
list.forEach(e -> e.setServiceAddress(serviceUtil.getServiceAddress()));
return list;
}
@Override
public void deleteRecommendation(int productId) {
repository.deleteAll(repository.findByProductId(productId));
}
}
RecommendationServiceImpl implementation:
@RestController
public interface RecommendationRepository extends CrudRepository {
List findByProductId(int productId);
}
What I've Tried:
Searched all files for /recommendation} using both IDE and grep -ri "/recommendation}" . Only found one match: ./target/surefire-reports/TEST-com.example.recommendation_service.RecommendationServiceApplicationTests.xml:/recommendation}
Cleaned and rebuilt the project (./mvnw clean install)
Verified all endpoint mappings exactly match /recommendation (no typos)
Confirmed the application runs successfully when started directly (only fails during tests)
Experimentally changed endpoint paths to /recommendation1, /recommendation2, etc. in both controller and tests
Error message still shows /recommendation} (without the numbers)
Key Observations
The error only appears when running tests
Deleting the test file makes the error disappear
The error appears in the test report XML, not in source code
Test File:
@SpringBootTest(webEnvironment = RANDOM_PORT)
class RecommendationServiceApplicationTests extends MongoDbTestBase {
@Autowired
private WebTestClient client;
@Autowired
private RecommendationRepository repository;
@BeforeEach
void setupDb() {
repository.deleteAll();
}
@Test
void getRecommendationsByProductId() {
int productId = 1;
postAndVerifyRecommendation(productId, 1, OK);
postAndVerifyRecommendation(productId, 2, OK);
postAndVerifyRecommendation(productId, 3, OK);
assertEquals(3, repository.findByProductId(productId).size());
getAndVerifyRecommendationsByProductId(productId, OK)
.jsonPath("$.length()").isEqualTo(3)
.jsonPath("$[2].productId").isEqualTo(productId)
.jsonPath("$[2].recommendationId").isEqualTo(3);
}
@Test
void duplicateError() {
int productId = 1;
int recommendationId = 1;
postAndVerifyRecommendation(productId, recommendationId, OK)
.jsonPath("$.productId").isEqualTo(productId)
.jsonPath("$.recommendationId").isEqualTo(recommendationId);
assertEquals(1, repository.count());
postAndVerifyRecommendation(productId, recommendationId, UNPROCESSABLE_ENTITY)
.jsonPath("$.message").isEqualTo("Duplicate key, Product Id: 1, Recommendation Id:1");
assertEquals(1, repository.count());
}
@Test
void deleteRecommendations() {
int productId = 1;
int recommendationId = 1;
postAndVerifyRecommendation(productId, recommendationId, OK);
assertEquals(1, repository.findByProductId(productId).size());
deleteAndVerifyRecommendationsByProductId(productId, OK);
assertEquals(0, repository.findByProductId(productId).size());
deleteAndVerifyRecommendationsByProductId(productId, OK);
}
@Test
void getRecommendationsMissingParameter() {
getAndVerifyRecommendationsByProductId("", BAD_REQUEST)
.jsonPath("$.path").isEqualTo("/recommendation");
}
@Test
void getRecommendationsInvalidParameter() {
getAndVerifyRecommendationsByProductId("?productId=no-integer", BAD_REQUEST)
.jsonPath("$.path").isEqualTo("/recommendation");
}
@Test
void getRecommendationsNotFound() {
getAndVerifyRecommendationsByProductId("?productId=113", OK)
.jsonPath("$.length()").isEqualTo(0);
}
@Test
void getRecommendationsInvalidParameterNegativeValue() {
int productIdInvalid = -1;
getAndVerifyRecommendationsByProductId("?productId=" + productIdInvalid, UNPROCESSABLE_ENTITY)
.jsonPath("$.message").isEqualTo("Invalid productId: " + productIdInvalid);
}
private WebTestClient.BodyContentSpec getAndVerifyRecommendationsByProductId(int productId, HttpStatus expectedStatus) {
return getAndVerifyRecommendationsByProductId("?productId=" + productId, expectedStatus);
}
private WebTestClient.BodyContentSpec getAndVerifyRecommendationsByProductId(String productIdQuery, HttpStatus expectedStatus) {
return client.get()
.uri("/recommendation" + productIdQuery)
.accept(APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(expectedStatus)
.expectHeader().contentType(APPLICATION_JSON)
.expectBody();
}
private WebTestClient.BodyContentSpec postAndVerifyRecommendation(int productId, int recommendationId, HttpStatus expectedStatus) {
Recommendation recommendation = new Recommendation(productId, recommendationId, "Author " + recommendationId, recommendationId, "Content " + recommendationId, "SA");
return client.post()
.uri("/recommendation")
.body(just(recommendation), Recommendation.class)
.accept(APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(expectedStatus)
.expectHeader().contentType(APPLICATION_JSON)
.expectBody();
}
private WebTestClient.BodyContentSpec deleteAndVerifyRecommendationsByProductId(int productId, HttpStatus expectedStatus) {
return client.delete()
.uri("/recommendation?productId=" + productId)
.accept(APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(expectedStatus)
.expectBody();
}
}
Question: How can there be a pattern parsing error for /recommendation} when my code clearly uses /recommendation without braces? Where should I look for this phantom pattern?