{"record":{"id":"532a378939eb2f0e","repo":"spring-projects/spring-ai","slug":"could-not-parse-text-search-score-doc-getstring","errorCode":null,"errorMessage":"Could not parse text search score: ${doc.getString(\"$score\")}","messagePattern":"Could not parse text search score: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/redis/RedisVectorStore.java","lineNumber":562,"sourceCode":"\t\t\ttry {\n\t\t\t\t// Text search scores can be very high (like 10.0), normalize to 0.0-1.0\n\t\t\t\t// range\n\t\t\t\tfloat textScore = Float.parseFloat(doc.getString(\"$score\"));\n\t\t\t\t// A simple normalization strategy - text scores are usually positive,\n\t\t\t\t// scale to 0.0-1.0\n\t\t\t\t// Assuming 10.0 is a \"perfect\" score, but capping at 1.0\n\t\t\t\tfloat normalizedTextScore = Math.min(textScore / 10.0f, 1.0f);\n\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"Text search raw score: \" + textScore + \", normalized: \" + normalizedTextScore);\n\t\t\t\t}\n\n\t\t\t\treturn normalizedTextScore;\n\t\t\t}\n\t\t\tcatch (NumberFormatException e) {\n\t\t\t\t// If we can't parse the score, fall back to default\n\t\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\t\tlogger.warn(\"Could not parse text search score: \" + doc.getString(\"$score\"));\n\t\t\t\t}\n\t\t\t\treturn 0.9f; // Default high similarity\n\t\t\t}\n\t\t}\n\n\t\t// Handle the case where the distance field might not be present (like in text\n\t\t// search)\n\t\tif (!doc.hasProperty(DISTANCE_FIELD_NAME)) {\n\t\t\t// For text search, we don't have a vector distance, so use a default high\n\t\t\t// similarity\n\t\t\tlogger.debug(\"No vector distance score found. Using default similarity.\");\n\t\t\treturn 0.9f; // Default high similarity\n\t\t}\n\n\t\tfloat rawScore = Float.parseFloat(doc.getString(DISTANCE_FIELD_NAME));\n\n\t\t// Different distance metrics need different score transformations\n\t\tif (logger.isDebugEnabled()) {","sourceCodeStart":544,"sourceCodeEnd":580,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/redis/RedisVectorStore.java#L544-L580","documentation":"RedisVectorStore.similarityScore parses the '$score' field from the Redis search result document into a float; on NumberFormatException it warns and falls back to a default similarity of 0.9f. This means the score string was malformed or absent, and the returned Similarity value is fabricated, not measured.","triggerScenarios":"Executing similaritySearch where the KNN '$score' attribute in the Redis reply cannot be parsed as a float — e.g. score field missing/empty, a non-numeric string, or locale/format issues.","commonSituations":"Custom Redis indexes missing the score attribute; mixing index definitions; Redis module (RediSearch) versions returning scores in unexpected format; queries executed against an index not created by the store.","solutions":["Verify the index was created by RedisVectorStore so the '$score' KNN attribute is present","Check the actual '$score' value in Redis (FT.SEARCH output) for malformed content","Upgrade spring-ai-redis-store / RediSearch to a version returning parseable numeric scores","Treat results with default 0.9 score with suspicion; re-run the query after fixing the index"],"exampleFix":"// before\n// score field missing -> silently 0.9\nList<Document> docs = vectorStore.similaritySearch(query);\n\n// after\n// recreate index via RedisVectorStore#afterPropertiesSet or check FT.INFO index\n// ensure KNN query includes return field \"$score\"","handlingStrategy":"type-guard","validationCode":"String score = doc.getString(\"$score\");\nif (score == null || !score.matches(\"-?\\\\d+(\\\\.\\\\d+)?\")) {\n    logger.warn(\"'$score' missing or non-numeric: \" + score);\n}","typeGuard":"boolean hasNumericScore(Document doc) {\n    String s = doc.getMetadata().get(\"$score\", \"\");\n    try { Float.parseFloat(s); return true; } catch (Exception e) { return false; }\n}","tryCatchPattern":"try {\n    List<Document> docs = redisVectorStore.similaritySearch(req);\n    docs.stream().filter(d -> !hasNumericScore(d)).forEach(d -> logger.warn(\"fabricated 0.9 score for \" + d.getId()));\n} catch (RuntimeException e) {\n    logger.error(\"Redis similarity search failed\", e);\n}","preventionTips":["Create the index through RedisVectorStore so '$score' is returned","Verify index definition with FT.INFO","Filter out documents whose score equals the 0.9 fallback when scores matter","Keep RediSearch module version aligned with the client library"],"tags":["redis","score-parsing","search","fallback"],"backgroundTag":"invalid-argument-format","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}