[ 
https://issues.apache.org/jira/browse/TINKERPOP-3270?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18098602#comment-18098602
 ] 

ASF GitHub Bot commented on TINKERPOP-3270:
-------------------------------------------

GumpacG opened a new pull request, #3559:
URL: https://github.com/apache/tinkerpop/pull/3559

   ## Summary
   
   `where(P)` expects a `String` scope key (e.g. `where(eq('a'))`). Given a 
non-String
   predicate value like `gt(5)`, `WherePredicateStep` cast it to `String` 
unconditionally,
   throwing a raw `ClassCastException` at step construction. This change 
validates the
   value first and throws a descriptive `IllegalArgumentException` pointing to 
`is(P)` for
   value comparisons. Backward-compatible diagnostics fix; valid queries are 
unaffected.
   
   ## Before vs After
   
   ### Non-String (numeric) predicate value
   
   ```
   __.values('v').where(gt(5))
   ```
   * Before: `ClassCastException: class java.lang.Integer cannot be cast to 
class java.lang.String`
   * After: `IllegalArgumentException: where(P) requires a String scope key but 
encountered Integer; use is(P) to compare values`
   
   ### Nested inside a parent `where(Traversal)`
   
   ```
   g.V().hasLabel('X').where(__.values('v').where(gt(5)))
   ```
   * Before: same `ClassCastException`, buried in a nested traversal
   * After: same descriptive `IllegalArgumentException` pointing to `is(P)`
   
   ### Correct usage (unchanged)
   
   ```
   g.V().as('a').out().where(eq('a'))   // String scope key comparison
   __.values('v').is(gt(5))             // value comparison via is(P)
   ```
   * Before and after: works identically
   
   ## Changes
   * `gremlin-core`: runtime `String` validation in 
`WherePredicateStep.getSelectKey(...)`
   * `gremlin-test`: negative tests added to the `WhereTest` suite (the 
exception is thrown at step construction, so they pass across all engines)
   * `CHANGELOG.asciidoc`: entry under 3.7.7
   
   Assisted-by: Kiro: Claude Opus 4.8




> `where(P)` throws `ClassCastException` for non-String predicate values 
> instead of a descriptive validation error
> ----------------------------------------------------------------------------------------------------------------
>
>                 Key: TINKERPOP-3270
>                 URL: https://issues.apache.org/jira/browse/TINKERPOP-3270
>             Project: TinkerPop
>          Issue Type: Bug
>          Components: process
>    Affects Versions: 3.7.6
>            Reporter: Lei Shu
>            Priority: Minor
>
> ### TinkerPop version
> Reproduced on:
> ```text
> TinkerGraph 3.7.0 — gremlin-server (verified 2026-07-12)
> TinkerGraph 3.8.1 — Docker tinkerpop/gremlin-server:latest (image 
> 60673820236b, built 2026-04-07)
> ```
> Source inspection against the 3.8.1 tag confirms the same unconditional 
> `(String)` cast is present.
> ### Environment
> * Host OS: Linux
> * Query language: Gremlin
> * Client: Gremlin Groovy script / gremlin-python WebSocket driver
> ### Description
> `where(P)` accepts a `P<String>` — the predicate value is expected to be a 
> scope key (a String label for a step or variable binding). Value comparisons 
> should use `is(P)` instead.
> When `where(P)` receives a non-string predicate such as `gt(5)`, 
> `WherePredicateStep` construction casts `predicate.getValue()` to `String` 
> without a runtime type check, producing a `ClassCastException`. The failure 
> occurs during step construction, before any data traversing begins; it does 
> not crash the Gremlin Server process.
> Although the Java generic signature is `where(P<String>)`, dynamically typed 
> Gremlin language variants and Gremlin Groovy scripts can construct and submit 
> this traversal at runtime. The raw internal exception obscures the actual API 
> misuse and makes troubleshooting unnecessarily difficult.
> This is not a request to make `where(gt(5))` work as a numeric filter — 
> `is(gt(5))` is the correct API for value comparisons.
> ### To reproduce
> The simplest trigger applies `where(gt(5))` directly to a value stream:
> ```groovy
> __.values('v').where(gt(5))
> // ClassCastException: Integer cannot be cast to String
> //   at WherePredicateStep.getSelectKey(WherePredicateStep.java:105)
> ```
> The same exception occurs when the invalid `where(P)` appears inside a parent 
> `where(Traversal)`:
> ```groovy
> g.V().drop()
> g.addV('X').property('v',10).iterate()
> g.addV('X').property('v',20).iterate()
> g.V().hasLabel('X').where(__.values('v').where(gt(5))).values('v').fold()
> // ClassCastException — same stack trace
> ```
> ### Expected behavior
> The invalid argument should be rejected with a descriptive argument or 
> type-validation exception that identifies the `where(P)` scope-key 
> requirement, rather than exposing an internal `ClassCastException`.
> ### Actual behavior
> `ClassCastException: class java.lang.Integer cannot be cast to class 
> java.lang.String`
> at `WherePredicateStep.getSelectKey(WherePredicateStep.java:105)`
> The exception message gives no indication of what the user did wrong or how 
> to correct it.
> ### Root cause
> During `WherePredicateStep` construction, `configurePredicates()` calls 
> `getSelectKey()`, which casts `predicate.getValue()` to `String` without 
> first validating its runtime type. In 3.8.1 the method has a `Collection` 
> branch as well, but neither path validates that the value selected as the 
> scope key is a `String` before casting:
> ```java
> // WherePredicateStep.java (3.8.1)
> public String getSelectKey(final P<?> predicate) {
>     if (predicate.getValue() instanceof Collection) {
>         // ... existing collection handling ...
>         return (String) ...;
>     }
>     return (String) predicate.getValue();  // line 105 — CCE when value is 
> Integer
> }
> ```
> The cast succeeds when `where(eq('a'))` is used (value = `"a"`, a valid scope 
> key). It fails when `where(gt(5))` is used (value = `5`, an `Integer`).
> ### Suggested fix
> Validate that the value selected by `getSelectKey()` is a `String` before 
> casting. The exact validation location and exception type are left to 
> maintainer discretion.
> ### Workaround
> Use `is()` instead of `where()` for value comparisons:
> ```groovy
> __.values('v').is(gt(5))  // correct
> g.V().hasLabel('X').where(__.values('v').is(gt(5))).values('v').fold()  // → 
> [10, 20]
> ```
> ### Cross-version verification
> | Version | Result |
> |---------|--------|
> | TinkerGraph 3.7.0 | ClassCastException |
> | TinkerGraph 3.8.1 | ClassCastException |
> ### Impact
> This primarily affects diagnostics for dynamically typed clients and remotely 
> submitted traversals. It does not produce incorrect query results, modify 
> data, or terminate the Gremlin Server process. A clear workaround (`is(P)`) 
> exists.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to