twilio / twilio-java

A Java library for communicating with the Twilio REST API and generating TwiML.
MIT License
492 stars 429 forks source link

ApiException: Unexpected character ('<' (code 60)) #608

Closed ValentinDenis closed 3 years ago

ValentinDenis commented 3 years ago

Issue Summary

This other issue is related to our issue: #604 We have a Java application that is using the SDK to make API calls to Twilio. Our logs go back to June and we can identify this issue every month since then. We created multiple support tickets that helped us set aside proxy related issues. It looks like it happens at least every 2 days, mainly on this getChannel call: https://www.twilio.com/docs/chat/rest/channel-resource#fetch-a-channel-resource (as it is the most called request on our app) We also found it happens when the API Helper is calling "PhoneNumberReader.read"

The last occurences happened on:

Steps to Reproduce

We tried to reproduce it by calling the method "getChannel" on a loop for 3 hours, but it did not yield any result.

Code Snippet

Here is our Java code calling the SDK:

     /**
     * @param channelPathId channelPathId can be either channel sid or channel unique name.
     */
    public Optional<ChannelWrapper> getChannel(String channelPathId) {
        try {
            Channel channel = Channel.fetcher(chatServiceSid, channelPathId).fetch(twilioRestClient);
            return Optional.of(new ChannelWrapper(channel));
        } catch (ApiException exception) {
            if (exception.getStatusCode() != null && exception.getStatusCode() == SC_NOT_FOUND) {
                return Optional.empty();
            } else {
                throw exception;
            }
        }
    }

Exception/Log

Exception on "getChannel":

[] [org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler] - Unexpected error occurred in scheduled task
com.twilio.exception.ApiException: Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: (ByteArrayInputStream); line: 1, column: 2]
    at com.twilio.exception.RestException.fromJson(RestException.java:57)
    at com.twilio.rest.chat.v2.service.ChannelFetcher.fetch(ChannelFetcher.java:56)
    at com.galerieslafayette.ps2.twilio.repository.TwilioChannelRepository.getChannel(TwilioChannelRepository.java:78)
    at com.galerieslafayette.ps2.twilio.repository.TwilioChannelRepository$$FastClassBySpringCGLIB$$4c6e9716.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
    at com.galerieslafayette.ps2.twilio.repository.TwilioChannelRepository$$EnhancerBySpringCGLIB$$c74e0ab8.getChannel(<generated>)
    at com.galerieslafayette.ps2.twilio.service.TwilioChannelService.getChannel(TwilioChannelService.java:34)
    at com.galerieslafayette.ps2.twilio.service.TwilioChannelService.getCustomerChannel(TwilioChannelService.java:75)
    at com.galerieslafayette.ps2.twilio.service.TwilioChannelService.lambda$getActiveCustomerChannelsSince$0(TwilioChannelService.java:49)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at com.galerieslafayette.ps2.twilio.service.TwilioChannelService.getActiveCustomerChannelsSince(TwilioChannelService.java:52)
    at com.galerieslafayette.ps2.backend.thirdparty.task.LackOfResponseToCustomerNotifier.execute(LackOfResponseToCustomerNotifier.java:78)
    at sun.reflect.GeneratedMethodAccessor434.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
    at net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor.executeWithLock(DefaultLockingTaskExecutor.java:54)
    at net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor.executeWithLock(DefaultLockingTaskExecutor.java:39)
    at net.javacrumbs.shedlock.core.DefaultLockManager.executeWithLock(DefaultLockManager.java:51)
    at net.javacrumbs.shedlock.core.LockableRunnable.run(LockableRunnable.java:35)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)
Caused by: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: (ByteArrayInputStream); line: 1, column: 2]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1840)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:712)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:637)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleUnexpectedValue(UTF8StreamJsonParser.java:2654)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._nextTokenNotInObject(UTF8StreamJsonParser.java:856)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:753)
    at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4340)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4189)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3242)
    at com.twilio.exception.RestException.fromJson(RestException.java:55)
    ... 42 common frames omitted

Exception on "PhoneNumberReader.read"

com.twilio.exception.ApiException: Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: (ByteArrayInputStream); line: 1, column: 2]
    at com.twilio.exception.RestException.fromJson(RestException.java:57)
    at com.twilio.rest.messaging.v1.service.PhoneNumberReader.pageForRequest(PhoneNumberReader.java:133)
    at com.twilio.rest.messaging.v1.service.PhoneNumberReader.firstPage(PhoneNumberReader.java:65)
    at com.twilio.rest.messaging.v1.service.PhoneNumberReader.read(PhoneNumberReader.java:46)
    at com.galerieslafayette.ps2.twilio.repository.TwilioPhoneNumberRepository.getPhoneNumbersResourceSet(TwilioPhoneNumberRepository.java:133)
    at com.galerieslafayette.ps2.twilio.repository.TwilioPhoneNumberRepository.getExistingPhoneNumbers(TwilioPhoneNumberRepository.java:120)
    at com.galerieslafayette.ps2.twilio.repository.TwilioPhoneNumberRepository.getAllEmployeePhoneNumbers(TwilioPhoneNumberRepository.java:103)
    at com.galerieslafayette.ps2.twilio.repository.TwilioPhoneNumberRepository$$FastClassBySpringCGLIB$$ac1cdeea.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
    at com.galerieslafayette.ps2.twilio.repository.TwilioPhoneNumberRepository$$EnhancerBySpringCGLIB$$3443bc12.getAllEmployeePhoneNumbers(<generated>)
    at com.galerieslafayette.ps2.usecase.phonenumber.PhoneNumberAssigner.findAvailablePhoneNumberForCustomer(PhoneNumberAssigner.java:76)
    at com.galerieslafayette.ps2.usecase.phonenumber.PhoneNumberAssigner.getAvailablePhoneNumber(PhoneNumberAssigner.java:71)
    at com.galerieslafayette.ps2.usecase.phonenumber.PhoneNumberAssigner.activateAndAssignPhoneNumber(PhoneNumberAssigner.java:47)
    at com.galerieslafayette.ps2.backend.thirdparty.service.PersonalStylistExternalIdentifierResolver.createAndAssignNewPhoneNumber(PersonalStylistExternalIdentifierResolver.java:80)
    at com.galerieslafayette.ps2.backend.thirdparty.service.PersonalStylistExternalIdentifierResolver.getOrAssignNewExternalIdentifier(PersonalStylistExternalIdentifierResolver.java:69)
    at com.galerieslafayette.ps2.backend.thirdparty.service.PersonalStylistExternalIdentifierResolver.byPersonalStylistAndCustomer(PersonalStylistExternalIdentifierResolver.java:47)
    at com.galerieslafayette.ps2.backend.thirdparty.usecase.message.OutgoingMessageSender.transferMessageToCustomer(OutgoingMessageSender.java:147)
    at com.galerieslafayette.ps2.backend.thirdparty.usecase.message.OutgoingMessageSender.execute(OutgoingMessageSender.java:112)
    at com.galerieslafayette.ps2.backend.thirdparty.listener.RabbitMQListener.executeMessageToCustomer(RabbitMQListener.java:93)
    at sun.reflect.GeneratedMethodAccessor390.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171)
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120)
    at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:53)
    at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:220)
    at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandlerAndProcessResult(MessagingMessageListenerAdapter.java:148)
    at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:133)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1579)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1498)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1486)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1477)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1421)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:963)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:913)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:81)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1284)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1190)
    at java.lang.Thread.run(Thread.java:748)
Caused by: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: (ByteArrayInputStream); line: 1, column: 2]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1840)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:712)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:637)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleUnexpectedValue(UTF8StreamJsonParser.java:2654)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._nextTokenNotInObject(UTF8StreamJsonParser.java:856)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:753)
    at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4340)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4189)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3242)
    at com.twilio.exception.RestException.fromJson(RestException.java:55)
    ... 45 common frames omitted

Technical details:

tigerfarm commented 3 years ago

When the following gives you the error, "Channel.fetcher(chatServiceSid, channelPathId).fetch(twilioRestClient)", if you capture the parameters, can you reproduce the error using those parameters (chatServiceSid and channelPathId)? try { Channel channel = Channel.fetcher(chatServiceSid, channelPathId).fetch(twilioRestClient); return Optional.of(new ChannelWrapper(channel)); } catch (ApiException exception) { if (exception.getStatusCode() != null && exception.getStatusCode() == SC_NOT_FOUND) { return Optional.empty(); } else { ... echo chatServiceSid and channelPathId ... throw exception; } }

ValentinDenis commented 3 years ago

Hello @tigerfarm

Thank you for taking the time to check our issue.

We could add the logging of chatServiceSid and channelPathId, but I'm not sure it would fix our issue here, it would just tell us the channel it occurs on, but since it occurs on other routes as well It wouldn't really help

eshanholtz commented 3 years ago

@ValentinDenis,

I think what @tigerfarm (please correct me if I'm misinterpreting) wants you to do by capturing the chatServiceSid and channelPathId is to try to reproduce a single instance of the exception. That helps us answer the question of whether the request would eventually succeed (may be a latency issue?).