Not able to store the attachment in the sys_attachment table
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
I have created a custom widget in ServiceNow and added it to a Record Producer using a custom type variable field. The purpose of this widget is to upload attachments to the sys_attachment table. Additionally, I have created Category (choice) field to associate a category with each attachment.
To achieve this, I opted for a custom widget instead of using the out-of-the-box (OOB) attachment widget. I also created a Script Include, which I’m calling from the widget’s client controller script.
However, when I click the attachment upload button, I encounter the error:
"Unhandled exception in GlideAjax".
I have already wrapped the logic in a try-catch block, but the error message remains unclear and doesn’t provide specific details.
Could you please advise on how to troubleshoot or resolve this issue?
widget code
html
<!-- Add Attachments Button -->
<button class="btn btn-link" ng-click="c.openModal()">
<i class="fa fa-paperclip"></i> Add attachments
</button>
<!-- Uploaded Attachments Display -->
<div ng-if="c.uploadedAttachments.length > 0" class="uploaded-attachments mt-3">
<div class="attachment-card" ng-repeat="attachment in c.uploadedAttachments track by $index">
<div class="attachment-info">
<div class="attachment-preview">
<!-- Show image preview if it's an image file -->
<img ng-if="attachment.isImage && attachment.previewUrl"
ng-src="{{attachment.previewUrl}}"
alt="{{attachment.name}}"
class="attachment-image">
<!-- Show file icon for non-image files -->
<div ng-if="!attachment.isImage" class="attachment-icon">
<i class="fa fa-file-o fa-2x"></i>
</div>
<!-- Loading state while reading image -->
<div ng-if="attachment.isImage && !attachment.previewUrl" class="attachment-loading">
<i class="fa fa-spinner fa-spin fa-2x"></i>
</div>
</div>
<div class="attachment-details">
<!--div class="attachment-name">{{attachment.name}}</div-->
<div class="attachment-meta">
<span class="file-name">{{attachment.displayFileName}}</span>
<span class="file-size">({{attachment.fileSize}})</span>
<span class="upload-time">{{attachment.uploadTime}}</span>
</div>
<div class="attachment-category" ng-if="attachment.category">
Category: {{attachment.category}}
</div>
</div>
</div>
<div class="attachment-actions">
<button type="button" class="btn btn-sm btn-link" ng-click="c.editAttachment($index)" title="Edit">
<i class="fa fa-pencil"></i>
</button>
<button type="button" class="btn btn-sm btn-link text-danger" ng-click="c.deleteUploadedAttachment($index)" title="Delete">
<i class="fa fa-times"></i>
</button>
</div>
</div>
</div>
<!-- Modal Window -->
<div class="modal" tabindex="-1" role="dialog" ng-show="c.modalOpen" style="display:block; background:rgba(0,0,0,0.3);">
<div class="modal-dialog" role="document">
<div class="modal-content" style="margin-top:80px;">
<div class="modal-header">
<h4>Attach file(s)</h4>
<button type="button" class="close" ng-click="c.closeModal()">×</button>
</div>
<div class="modal-body">
<!-- Drag and Drop Area -->
<div class="upload-zone text-center p-4 mb-3 border-dashed"
ng-drop="true"
ng-drop-success="c.handleDrop($event)">
<i class="fa fa-paperclip fa-2x"></i>
<div>Drag and drop files here</div>
</div>
<div class="text-center my-2">OR</div>
<!-- Select Files Button -->
<button class="btn btn-secondary" type="button" ng-click="c.selectFiles()">
Select file(s)
</button>
<input type="file" id="fileInput" multiple style="display:none"
onchange="angular.element(this).scope().c.handleFileSelect(this.files)">
<!-- Attachments Table -->
<div ng-if="c.attachments.length > 0" class="mt-4">
<table class="table table-sm">
<thead>
<tr>
<th>Preview</th>
<th>Name</th>
<th>File</th>
<th>Category</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="a in c.attachments">
<td>
<img ng-if="a.isImage && a.previewUrl" ng-src="{{a.previewUrl}}" class="table-preview-image">
<i ng-if="!a.isImage" class="fa fa-file-o"></i>
<i ng-if="a.isImage && !a.previewUrl" class="fa fa-spinner fa-spin"></i>
</td>
<td>
<input class="form-control" ng-model="a.name" ng-change="c.updateFileName(a)" required>
</td>
<td>{{a.displayFileName}}</td>
<td>
<select class="form-control" ng-model="a.category" ng-options="cat for cat in c.categories"></select>
</td>
<td>
<button type="button" class="btn btn-danger btn-sm" ng-click="c.removeAttachment($index)">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" ng-click="c.closeModal()">Cancel</button>
<button class="btn btn-primary" ng-disabled="!c.attachments.length" ng-click="c.uploadAttachments()">Attach</button>
</div>
</div>
</div>
</div>
client controller
script include code
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
Hi @Praju_123 ,
The code you pasted is very big and hard to understand in this format. But still there are some reason for the "Unhandled exception in GlideAjax" which means that a GlideAjax call from a Client Script/UI Policy/UI Action tried to call a Script Include, but something broke on the server-side response.
Reasons:
- Check Script Include if it is not Client Callable-If you want to use a Script Include in GlideAjax, it must be:Active,Client Callable = true,Extend AbstractAjaxProcessor.
- Method Name Mismatch-The value of sysparm_name must exactly match a function in the Script Include.
- Server-side Error Inside Script Include-Check System Logs > All for a stack trace.
- Script Include Not Accessible in Scope-If you’re in a scoped app, but the Script Include is global (or vice versa), you might hit scope access issues.Fix: Check "Accessible from" in the Script Include.
- Return Type is Wrong-Sometimes we try return inside the method instead of return "value"; at the right place.For getXMLAnswer(), you must return stringValue; from Script Include.
Check these few things in your script. Please mark helpful.