GoogleCloudPlatform / GoogleCloudPlatform/spring-cloud-gcp
Misleading Error Message when Upserting JSON Double Values using SpannerTemplate
- Dominant language
- Java
- Stars
- 551
- Forks
- 349
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 14
Description
**Edit:** Looking closer to the code it seems that the information is already lost when spring-gcp gets the response from Spanner. However since the information exists _somehwere_ I wonder if you could make this more transparent.
---
The [Google Cloud Spanner documentation](https://cloud.google.com/spanner/docs/working-with-json) outlines the requirements for handling Double values in JSON. However, when attempting to upsert a Double value inside JSON using the SpannerTemplate, a confusing error message occurs, despite Spanner's direct handling of JSON Double inputs providing more descriptive feedback.
### Steps to Reproduce:
Create a table using the following SQL:
```sql
CREATE TABLE Test (
ID STRING(MAX) NOT NULL,
DATA JSON
) PRIMARY KEY(ID);
```
Upsert the following Kotlin entity using SpannerTemplate:
```kotlin
val entity = TestEntity(
id = "1",
data = NumberData(297.3344693281405)
)
spannerTemplate.upsert(entity)
```
**Expected Behavior:**
A clear and descriptive error message explaining the issue with the Double value, similar to direct SQL interactions with Spanner (e.g., "Invalid JSON literal: Input number: 297.3344693281405 cannot round-trip through string representation").
**Actual Behavior:**
The following misleading error message is generated:
> com.google.cloud.spanner.SpannerException: FAILED_PRECONDITION: io.grpc.StatusRuntimeException: FAILED_PRECONDITION: Invalid value for column DATA in table Test: Expected JSON.
### Additional Context:
Directly executing the following query in Spanner produces a more informative error message:
```sql
SELECT JSON '297.3344693281405'
```
> Statement failed: Invalid JSON literal: Input number: 297.3344693281405 cannot round-trip through string representation
If the number is wrapped inside a JSON object, a similarly helpful error message is returned:
```sql
SELECT JSON '{"foo": 297.3344693281405 }';
```
> Invalid JSON literal: Input number: 297.3344693281405 cannot round-trip through string representation
### Request:
Please ensure that SpannerTemplate provides a more accurate error message when handling JSON Double values to avoid confusion and aid in debugging.
### Appendix:
Full StackTrace:
```
FAILED_PRECONDITION: io.grpc.StatusRuntimeException: FAILED_PRECONDITION: Invalid value for column DATA in table Test: Expected JSON.
com.google.cloud.spanner.SpannerException: FAILED_PRECONDITION: io.grpc.StatusRuntimeException: FAILED_PRECONDITION: Invalid value for column DATA in table Test: Expected JSON.
at app//com.google.cloud.spanner.SpannerExceptionFactory.newSpannerExceptionPreformatted(SpannerExceptionFactory.java:309)
at app//com.google.cloud.spanner.SpannerExceptionFactory.fromApiException(SpannerExceptionFactory.java:329)
at app//com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException(SpannerExceptionFactory.java:181)
at app//com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException(SpannerExceptionFactory.java:110)
at app//com.google.cloud.spanner.SpannerExceptionFactory.asSpannerException(SpannerExceptionFactory.java:100)
at app//com.google.cloud.spanner.TransactionRunnerImpl$TransactionContextImpl$CommitRunnable.lambda$run$0(TransactionRunnerImpl.java:458)
at app//com.google.common.util.concurrent.DirectExecutor.execute(DirectExecutor.java:31)
at app//com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1300)
at app//com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:1061)
at app//com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:811)
at app//com.google.api.gax.retrying.BasicRetryingFuture.handleAttempt(BasicRetryingFuture.java:202)
at app//com.google.api.gax.retrying.CallbackChainRetryingFuture$AttemptCompletionListener.handle(CallbackChainRetryingFuture.java:135)
at app//com.google.api.gax.retrying.CallbackChainRetryingFuture$AttemptCompletionListener.run(CallbackChainRetryingFuture.java:117)
at app//com.google.common.util.concurrent.DirectExecutor.execute(DirectExecutor.java:31)
at app//com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1300)
at app//com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:1061)
at app//com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:811)
at app//com.google.api.core.AbstractApiFuture$InternalSettableFuture.setException(AbstractApiFuture.java:92)
at app//com.google.api.core.AbstractApiFuture.setException(AbstractApiFuture.java:74)
at app//com.google.api.gax.grpc.GrpcExceptionCallable$ExceptionTransformingFuture.onFailure(GrpcExceptionCallable.java:97)
at app//com.google.api.core.ApiFutures$1.onFailure(ApiFutures.java:84)
at app//com.google.common.util.concurrent.Futures$CallbackListener.run(Futures.java:1130)
at app//com.google.common.util.concurrent.DirectExecutor.execute(DirectExecutor.java:31)
at app//com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1300)
at app//com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:1061)
at app//com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:811)
at app//io.grpc.stub.ClientCalls$GrpcFuture.setException(ClientCalls.java:568)
at app//io.grpc.stub.ClientCalls$UnaryStreamToFuture.onClose(ClientCalls.java:538)
at app//io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
at app//io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
at app//io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
at app//com.google.api.gax.grpc.ChannelPool$ReleasingClientCall$1.onClose(ChannelPool.java:569)
at app//io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
at app//io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
at app//io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
at app//io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
at app//io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
at app//io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
at app//com.google.cloud.spanner.spi.v1.SpannerErrorInterceptor$1$1.onClose(SpannerErrorInterceptor.java:100)
at app//io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:564)
at app//io.grpc.internal.ClientCallImpl.access$100(ClientCallImpl.java:72)
at app//io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:729)
at app//io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:710)
at app//io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at app//io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
at java.base@17.0.10/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base@17.0.10/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base@17.0.10/java.lang.Thread.run(Thread.java:840)
Caused by: io.grpc.StatusRuntimeException: FAILED_PRECONDITION: Invalid value for column DATA in table Test: Expected JSON.
at app//io.grpc.Status.asRuntimeException(Status.java:532)
... 21 more
```
Contributor guide
Assessment
This issue has not been assessed yet.