GitHub repository with the Mule project can be found at the end of the post.
Are you familiar with the fromBase64 or the toBase64 functions from DataWeave 2.0? What about the getUrlEncoder or the getEncoder functions from Java? Do you know the differences between the “basic” Base 64 encoding and the “URL and Filename safe” Base 64 encoding? Well, you may have guessed it by now, but you’re about to find out the answers to these questions!
Or maybe you’re here because you keep getting the “Illegal base64 character” error in DataWeave. Even if you get this error using any other programming language, this post can help you understand why it is happening.
The Problem
I was recently presented with this problem when Maria Isabel Vargas asked a question about base64 decoding in a Slack channel. She was using the fromBase64 function that’s available in Mule 4 - DataWeave 2.0 inside the dw::core::Binaries module to transform a basic Base 64 string into a binary value.
The problem was that the server was returning a Base 64 URL Encoded String (e.g. “cHJvc3RkZXY_YmxvZw==”) opposed to the basic Base 64 string from which the fromBase64 function attempts to transform. As a result, the Transform Message component was returning this error: “Illegal base64 character 5f". This error happens when the string that you are trying to transform contains a character not recognized by the basic Base 64 Alphabet (in this case it was an underscore character). Below you can see which characters are accepted.
Since the server was using the Base 64 URL encoding, the string that we were trying to decode in DataWeave contained different characters from the ones above because the Base 64 URL has a different alphabet. You can see this alphabet below.
Notice that the characters 62 and 63 differ from the basic Base 64 Alphabet and the Base 64 URL Alphabet. The first one contains the characters plus (+) and slash (/), while the second one uses the characters minus (-) and underline (_).
In other words, if you have an encoded string like "cHJvc3RkZXY_YmxvZw==", you wouldn’t be able to transform it using a basic Base64 decoder because it contains characters that are not recognized by its alphabet (the underline character).
You can get these two errors when you try to transform a Base 64 URL string using the fromBase64 DW function (at least when this post was created):
java.lang.IllegalArgumentException: Illegal base64 character 5f (when containing an underscore)
java.lang.IllegalArgumentException: Illegal base64 character 2d (when containing a minus)
The Solution
Java contains a function called getUrlDecoder, which is used to decode a string that was encoded using the Base 64 URL alphabet. This is exactly the problem that we were trying to solve. But before you start panicking and coding your solution using Java and creating a whole Java class, it's way easier to just use the Mule 4 Scripting Module. I’ll show you how.
This module should already be installed in your Studio. However, if you can’t see it, you can download it from Exchange. Alternatively, you can use the “Add Modules” button from Anypoint Studio, which is located on your Mule Palette.
After that, you can simply take the Execute component and drag-and-drop it into your flow. For this demonstration, I will be using an HTTP Listener as a trigger, and I will send the encoded string in the body of the request from Postman.
Once you have the Execute component, you can select “Groovy” as your Engine and paste the following line of code into your script:
Base64.getUrlDecoder().decode(payload)
You should end up with this:
As you can see in this Groovy script, we are using the getUrlDecoder function to decode whatever is in our payload. The result will be returned to our Postman application in the body of the response.
You need to make sure that the payload that is sent to the Groovy script is a Java String. So, let’s add a Transform Message right after the HTTP Listener and transform the payload into a Java string. Like this:
Let’s start this Mule app and test!
We send the same string as before (cHJvc3RkZXY_YmxvZw==) from Postman, and we expect to get the decoded message as a response.
And… it worked!
Try it yourself
Do you want to experiment a bit more with all of this? For sure! Let me give you a quick guide on how to do this.
1. Encode the bytes/string into a Basic 64 URL encoded string
You will need Java (or you can also do it in Mule 4 using a Groovy script) to get the encoded string first. You don’t need to download and install Java onto your computer to do this; you can search for a free online Java compiler like this one: TutorialsPoint Java Compiler
There, just copy and paste this Java code:
Next, click on “Execute” in the top left corner. You will see the encoded string on the right side of the website (the last line in the white background). Copy this string and save it somewhere so that you can use it in the next steps.
2. Create your Mule App in Anypoint Studio
Follow the steps explained previously in “The Solution”, or paste this code into the XML configuration of your file:
3. Create a Postman request
If you don’t have Postman, you can download it for free from here: Postman Download. When you open it, you just have to click on the plus (+) button as you can see here:
Then, add this in the Request URL section:
localhost:8081/flow1
And add the previously copied string inside the Request Body. Don’t forget to select “Raw” and “Text” from the two dropdowns that are over the request body text area.
4. Run your Mule App
Once it is deployed successfully, you can click on the big blue “Send” button from Postman and see the results which should match the string you encoded in Step 1 (using Java).
Recap
Let’s do a quick recap on what we just learned from this post:
The basic Base 64 alphabet can recognize the characters plus (+) and slash (/).
The Base 64 URL alphabet can recognize the characters minus (-) and underline (_).
Depending on how a string was encoded (using basic Base 64 or Base 64 URL encoding), it can include any of the characters from the chosen alphabet.
An encoded string with one alphabet may not be decoded using the other alphabet’s decoding system. If the decoder attempts to read an unrecognized character, it’ll send an Illegal base64 character error message.
You can’t decode a Base 64 URL string with the fromBase64 DataWeave 2.0 function.
You can decode a Base 64 URL string by using an Execute component along with a Groovy engine and the getUrlDecoder Java function.
Had you seen this error before? How did you solve it?
Sign-up to leave a comment below!
Prost!
-Alex
Hi Alex,
I am trying to encode pdf containing some text and images/logos to base 64. I need to send endoded base 64 content to external system. I tried to encode pdf with tobase() function in dataweave and using the java function which you have given in your blog. But base64 content generated using these two ways is not correct. When external system is generating pdf from base 64 content , the resulting pdf is showing error as 'Message: Illegal character: 41' . Basically images/logos in PDF are not loading properly When I send base 64 content generated using online tool (https://base64.guru/converter/encode/pdf). then external system is able to generate PDF properly from it.
Hi Alexandra
This is super helpful!! It worked like a charm :) I am glad that you helped me to resolve ths issue on priority and also you have given all the related links and explanations to help in the concept intake. Loving to find out this team on the interwebs ! Thank you so very much !
Warm Regards
Tanushree
Hi Alexendra
I have recieved your response via email so writing again. I am receiving below payload from an external system :
payload=%7B%22type%22%3A%22block_actions%22%2C%22user%22%3A%7B%22id%22%3A%22U01M26265NU%22%2C%22username%22%3A%22connect2tanushree%22%2C%22name%22%3A%22connect2tanushree%22%2C%22team_id%22%3A%22T01LCAPH1BL%22%7D%2C%22api_app_id%22%3A%22A01L5J2FV8W%22%2C%22token%22%3A%22u2AkH2O8aShNqskXd1S464z5%22%2C%22container%22%3A%7B%22type%22%3A%22message%22%2C%22message_ts%22%3A%221612281350.000100%22%2C%22channel_id%22%3A%22D01LT6C44F3%22%2C%22is_ephemeral%22%3Afalse%7D%2C%22trigger_id%22%3A%221781264358804.1692363579394.aa5a08357f8700923ea6e05f4164de4d%22%2C%22team%22%3A%7B%22id%22%3A%22T01LCAPH1BL%22%2C%22domain%22%3A%22workdaytestteamgroup%22%7D%2C%22enterprise%22%3Anull%2C%22is_enterprise_install%22%3Afalse%2C%22channel%22%3A%7B%22id%22%3A%22D01LT6C44F3%22%2C%22name%22%3A%22directmessage%22%7D%2C%22message%22%3A%7B%22bot_id%22%3A%22B01KZPXR02K%22%2C%22type%22%3A%22message%22%2C%22text%22%3A%22Important+Message+%22%2C%22user%22%3A%22U01LBEQRAAH%22%2C%22ts%22%3A%221612281350.000100%22%2C%22team%22%3A%22T01LCAPH1BL%22%2C%22blocks%22%3A%5B%7B%22type%22%3A%22header%22%2C%22block_id%22%3A%22bBeX%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Track+Your+Time%22%2C%22emoji%22%3Atrue%7D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22r271%22%2C%22fields%22%3A%5B%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22%2AGeneral+%26amp%3B+Administrative%3A%2A%22%2C%22verbatim%22%3Afalse%7D%5D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22rSrvb%22%2C%22text%22%3A%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22+%22%2C%22verbatim%22%3Afalse%7D%2C%22accessory%22%3A%7B%22type%22%3A%22static_select%22%2C%22action_id%22%3A%22static_select-action%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Select+Number+of+Hours%22%2C%22emoji%22%3Atrue%7D%2C%22options%22%3A%5B%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%220%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-0%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%2240%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-1%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%2232%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-2%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%2224%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-3%22%7D%5D%7D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22694Ex%22%2C%22fields%22%3A%5B%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22%2AProjects%3A%2A%22%2C%22verbatim%22%3Afalse%7D%5D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22yOkLb%22%2C%22text%22%3A%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22Country+Expansion+ATP%22%2C%22verbatim%22%3Afalse%7D%2C%22accessory%22%3A%7B%22type%22%3A%22static_select%22%2C%22action_id%22%3A%22static_select-action%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Select+Number+of+Hours%22%2C%22emoji%22%3Atrue%7D%2C%22options%22%3A%5B%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%220%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-0%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%2240%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-1%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%2232%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-2%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%2224%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-3%22%7D%5D%7D%7D%2C%7B%22type%22%3A%22section%22%2C%22block_id%22%3A%22Led%22%2C%22fields%22%3A%5B%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22%2AWhen%3A%2A%5CnJan+31+-+Feb+06%22%2C%22verbatim%22%3Afalse%7D%5D%7D%2C%7B%22type%22%3A%22actions%22%2C%22block_id%22%3A%22eA%5C%2F%22%2C%22elements%22%3A%5B%7B%22type%22%3A%22button%22%2C%22action_id%22%3A%22V2%2Bep%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Submit+Timesheet%22%2C%22emoji%22%3Atrue%7D%2C%22style%22%3A%22primary%22%2C%22value%22%3A%22click_me_123%22%7D%5D%7D%5D%7D%2C%22state%22%3A%7B%22values%22%3A%7B%22rSrvb%22%3A%7B%22static_select-action%22%3A%7B%22type%22%3A%22static_select%22%2C%22selected_option%22%3A%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%2240%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-1%22%7D%7D%7D%2C%22yOkLb%22%3A%7B%22static_select-action%22%3A%7B%22type%22%3A%22static_select%22%2C%22selected_option%22%3A%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%220%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22value-0%22%7D%7D%7D%7D%7D%2C%22response_url%22%3A%22https%3A%5C%2F%5C%2Fhooks.slack.com%5C%2Factions%5C%2FT01LCAPH1BL%5C%2F1775098058146%5C%2FbJ3EryWaj4hHIt0grYdt102B%22%2C%22actions%22%3A%5B%7B%22action_id%22%3A%22V2%2Bep%22%2C%22block_id%22%3A%22eA%5C%2F%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Submit+Timesheet%22%2C%22emoji%22%3Atrue%7D%2C%22value%22%3A%22click_me_123%22%2C%22style%22%3A%22primary%22%2C%22type%22%3A%22button%22%2C%22action_ts%22%3A%221613828122.887562%22%7D%5D%7D
My goal is to convert this to json. For this I did the same scripting tool->groovy script execution by feeding java input of this payload. I am receiving below error when posting from postman:
java.lang.IllegalArgumentException: Illegal base64 character 25
Please let me know how can I fix this.
Thank Yiu
Tanushree
Thanks for this post @Alex, for URL encoding do we need to use Java? Is there any option to generate byte array from a string payload? If yes, then it seems we can use Base64.getUrlencoder().encode(payload)