java.lang.IllegalArgumentException: Invalid character ':' in key name ':status'
Hi, thank you for your work on this library !
I've started experimenting with various error cases and one of them crashes the library at least on Desktop/JVM:
déc. 05, 2025 4:42:54 PM io.grpc.internal.ClientCallImpl closeObserver
AVERTISSEMENT: Exception thrown by onClose() in ClientCall
java.lang.IllegalArgumentException: Invalid character ':' in key name ':status'
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:273)
at io.grpc.Metadata$Key.validateName(Metadata.java:754)
at io.grpc.Metadata$Key.<init>(Metadata.java:762)
at io.grpc.Metadata$Key.<init>(Metadata.java:671)
at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:971)
at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:966)
at io.grpc.Metadata$Key.of(Metadata.java:708)
at io.grpc.Metadata$Key.of(Metadata.java:704)
at io.github.timortel.kmpgrpc.core.ConversionutilKt.getKmMetadata(conversionutil.kt:40)
at io.github.timortel.kmpgrpc.core.internal.ClientInterceptorImpl$interceptCall$1$start$1.onClose(ClientInterceptorImpl.kt:69)
at io.grpc.internal.DelayedClientCall$DelayedListener$3.run(DelayedClientCall.java:487)
at io.grpc.internal.DelayedClientCall$DelayedListener.delayOrExecute(DelayedClientCall.java:451)
at io.grpc.internal.DelayedClientCall$DelayedListener.onClose(DelayedClientCall.java:484)
at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:565)
at io.grpc.internal.ClientCallImpl.access$100(ClientCallImpl.java:72)
at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:733)
at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:714)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
This error occurs when I try to call a server responding with 502 Bad Gateway. My backend configuration uses a proxy pointing to a GRPC server. When the GRPC server is down, the proxy returns 502.
The onClose operation seems to happen on a separate thread, so there is no way for me to catch this exception.
Thank you for reporting this problem. Seems like the parsing of the key value pair causes the crash. I will have a look at this problem.
Can you maybe describe your setup in a bit more detail? Which grpc version and flavor (Java, Python, etc) is your server using?
It should be simple to reproduce, because you don't even need a GRPC server. I reproduced the error by using the https://example.com url for building the grpc channel:
Channel.Builder.forAddress("example.com", 443).build()
The exception occurs when the server the client connects to understands HTTP/2, but is not a GRPC server.
I have the same error as soon as I add an interceptor, if the backend is unavailable (orwith example.com which is http 2.0 without GRPC). The issue happens even if I just return the original metadata or add some log like in the example. If I remove the interceptor, I have a proper statusexception as a response: io.github.timortel.kmpgrpc.core.StatusException: Status(code=UNAVAILABLE, statusMessage=HTTP status code 503 or something like that.
With an interceptor I have the IllegalArgumentException from io.grpc/conversionutil.kt.
I guess somewhere the HTTP/2 pseudo-headers should be filtered. Filtering in onClose, does not solve it, because the error happens before that is called, I think probably conversionutil.kt JvmMetadata.kmMetadata is the issue.
Until this is fixed I had to create the channel in JVM/Android by building a Managed channel with it's own client interceptor and hack into this library via reflection to call the private constructor of Channel.
@TimOrtel Can you take a look?