Hello @zebra_taxi
I was able to reproduce the issue with a similar setup.
The error appears to be caused by reusing the fileKey from the attachment field in the source record.
The fileKey retrieved from an existing Attachment field cannot be used directly to attach that file to another record. To copy the attachment, you first need to download the original file using its fileKey, upload the file again using the Upload File API, and then use the new fileKey returned by the Upload File API when creating the record in the guest space app.
Also, the File Download and Upload APIs cannot be called using kintone.api(), so you can use XMLHttpRequest or the Fetch API for those requests.
I tested the following approach with a source app and an app inside a guest space, and I was able to copy both the field value and attachment successfully:
(function () {
'use strict';
kintone.events.on('app.record.detail.process.proceed', function (event) {
if (event.nextStatus.value !== 'Synced to Guest Space') {
return event;
}
const record = event.record;
if (!record.file.value.length) {
event.error = 'No attachment was found.';
return event;
}
const sourceFile = record.file.value[0];
// Download the original attachment
const downloadFile = function () {
return new Promise(function (resolve, reject) {
const params = {
fileKey: sourceFile.fileKey
};
const url = kintone.api.urlForGet('/k/v1/file.json', params);
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.responseType = 'blob';
xhr.onload = function () {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(new Error('File download failed.'));
}
};
xhr.onerror = function () {
reject(new Error('File download failed.'));
};
xhr.send();
});
};
// Upload the file again to obtain a new fileKey
const uploadFile = function (blob) {
return new Promise(function (resolve, reject) {
const formData = new FormData();
formData.append('__REQUEST_TOKEN__', kintone.getRequestToken());
formData.append('file', blob, sourceFile.name);
const xhr = new XMLHttpRequest();
xhr.open('POST', '/k/v1/file.json');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onload = function () {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error('File upload failed.'));
}
};
xhr.onerror = function () {
reject(new Error('File upload failed.'));
};
xhr.send(formData);
});
};
return downloadFile()
.then(function (blob) {
return uploadFile(blob);
})
.then(function (uploadResponse) {
const body = {
app: 26,
record: {
guest_title: {
value: record.title.value
},
guest_summary: {
value: record.summary.value
},
guest_file: {
value: [
{
fileKey: uploadResponse.fileKey
}
]
}
}
};
return kintone.api(
'/k/guest/3/v1/record',
'POST',
body
);
})
.then(function (response) {
console.log('Copied to guest space:', response);
return event;
})
.catch(function (error) {
console.error('Sync failed:', error);
event.error =
'The record could not be synced to the guest space app.';
return event;
});
});
})();
There is also a separate reason why the status proceeded even though the sync failed in your original code.
Your catch() block logs the error and then returns the normal event object:
.catch(function(err) {
console.error(err);
return event;
});
This allows the Process Management action to continue. If you want to prevent the status from changing when the synchronization fails, you can set event.error in the catch() block before returning the event, as shown in the sample above.
With this approach, the record is synced first, and the status proceeds only when the synchronization completes successfully.
Please note that the sample follows your current logic of copying only the first attachment. If you need to copy multiple attachments, the file download/upload process would need to be repeated for each file.
I hope this helps.