# Inbound SOAP/Base64 attachments stopped working

An issue with inbound attachments being saved using this mechanism has been seen in ServiceNow instances upgrading to Quebec and later which are using base64 encoded attachments. An example of this error is shown below:

{% hint style="danger" %}
InternalError: The choice of Java method com.glide.ui.SysAttachment.write matching JavaScript argument types (object,org.mozilla.javascript.ConsString,string,null) is ambiguous; candidate methods are:

class java.lang.String write(com.glide.script.GlideRecord,java.lang.String,java.lang.String,java.io.InputStream)

class java.lang.String write(com.glide.script.GlideRecord,java.lang.String,java.lang.String,java.io.File)

class java.lang.String write(com.glide.script.GlideRecord,java.lang.String,java.lang.String,java.lang.String)

class java.lang.String write(com.glide.script.GlideRecord,java.lang.String,java.lang.String,byte\[])
{% endhint %}

Unifi comes packaged with a utility for helping save attachments:

`AttachmentHandler.saveAttachment(record, filename, content_type, data)`

There has been a change to the `GlideSysAttachment` API being used by this method. Now, passing the base64 decoded string as a byte array to the global `GlideSysAttachment.write()` method via the Unifi AttachmentHandler appears to only work when Unifi trace mode is on. Internal investigation has revealed that there is some interaction between ServiceNow and the Unifi scoped app which is causing the byte array to be converted to null when Unifi trace mode is off. We suspect this is because ServiceNow routes the variable through some coercion in the Rhino environment when passing it through Unifi and that forces it to work. Without the debug mode turned on, the byte array is being passed through and, for some reason that isn't clear, it is treated differently.

### Workaround

To work around this issue it is recommended to modify the Extract attachments script on the message which is being used to process attachments. By adding a new `saveAttachments()` function that makes use of ServiceNow's `writeBase64()` method, the data does not need to be decoded like it was in the past and attachments are processed properly regardless of Unifi settings.

```javascript
function saveAttachment(record, filename, content_type, data) {
  var id;

  if (typeof filename === 'undefined') {
    x_snd_eb.ws_console.warn('Ignoring attachment with missing filename attribute.');
    id = 'x-attachment-error-missing-filename';
  } else if (!filename) {
    x_snd_eb.ws_console.warn('Ignoring attachment with empty filename attribute.');
    id = 'x-attachment-error-filename-is-empty';
  } else {
    // use x_snd_eb scope to force using the scoped version of GSA
    id = new x_snd_eb.GlideSysAttachment().writeBase64(record, filename, content_type, data);
    x_snd_eb.ws_console.trace('Extracted attachment [' + filename + ':' + content_type + ']' +
      ' to record [' + id + ']' +
      ' for [' + record.getTableName() + ':' + record.sys_id + ']');
  }

  // return the original tag with the sys_id of the new attachment record
  // so we can pick it up in processing later on
  return '<x-attachment-data sys_id="' + id + '" />';
}
```

### Binary Base64

Decoding a base64 string should be done with binary aware methods to ensure that binary files are decoded correctly. We have seen problems using methods like GlideStringUtil.base64Decode() with binary files where the attachment is created but it is not usable. This is likely the problem if plain text files are working but images are not.

### Full example for `Extract attachments script`

This example is for a JSON payload, but the same concept applies to SOAP.

```javascript
payload = (function extractAttachments(payload, request) {

  function saveAttachment(record, filename, content_type, data) {
    var id;

    if (typeof filename === 'undefined') {
      x_snd_eb.ws_console.warn('Ignoring attachment with missing filename attribute.');
      id = 'x-attachment-error-missing-filename';
    } else if (!filename) {
      x_snd_eb.ws_console.warn('Ignoring attachment with empty filename attribute.');
      id = 'x-attachment-error-filename-is-empty';
    } else {  
      // use x_snd_eb scope to force using the scoped version of GSA
      id = new x_snd_eb.GlideSysAttachment().writeBase64(record, filename, content_type, data);
      x_snd_eb.ws_console.trace('Extracted attachment [' + filename + ':' + content_type + ']' +
        ' to record [' + id + ']' +
        ' for [' + record.getTableName() + ':' + record.sys_id + ']');
    }

    // return the original tag with the sys_id of the new attachment record
    // so Unifi can pick it up in processing later on
    return '[x-attachment-data sys_id="' + id + '" /]';
  }

  var obj = JSON.parse(payload);

  obj.attachment.data = saveAttachment(
    request, 
    obj.attachment.filename, 
    obj.attachment.mime_code, 
    obj.attachment.data
  );

  return JSON.stringify(obj);
})(payload, request);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sharelogic.com/unifi/4.2/troubleshooting/attachments/embedded-attachments-not-being-sent.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
