[SalesForce] “Note can’t be saved” error on ContentNote insert

I'm trying to create ContentNote records from Note ones but it's always giving me this particular error:

FATAL_ERROR System.UnexpectedException: Note can't be saved

Code:

List<Note> noteLIST = [SELECT Id, Title, Body, OwnerId, Owner.Profile.Name, ParentId, IsPrivate, CreatedById, CreatedDate FROM Note WHERE Owner.Profile.Name LIKE 'current_profile_i_want_to_check'];
List<ContentNote> cnLIST = new List<ContentNote>();
for(Note n : noteLIST){
    ContentNote cn = new ContentNote(Title = n.Title,
                                     Content = Blob.valueOf(n.Body));
    cnLIST.add(cn);
}
if(!cnLIST.isEmpty()){
    insert cnLIST;
}

I've checked this post: Help with "System.UnexpectedException: Note can't be saved" in batch apex but still not working.

Best Answer

I'm the author of the ContentNote library referred to in the linked answer. I'll reiterate the things I'm aware of that you need to do to prepare note content for insertion:

  1. Replace all basic HTML characters (<>"'&) with their corresponding entities (&amp; and friends).
  2. Replace all line breaks with <br> (taking care with Windows CRLF/Linux LF/Mac CR)
  3. Replace &apos; with &#39;.
  4. Do not replace Unicode characters with entities. Other entities, including &apos;, result in an exception. Unicode should be left as the bare characters.
  5. Ensure that the source content is well-formed Unicode/UTF-8 and does not contain non-printable characters.
  6. The title must not be null, zero-length, or consist only of whitespace. The title need not be escaped.

Also note that

the API reference on ContentNote incorrectly specifies to use String.escapeHTML4() to prepare content. This does not work.

Here's how I actually prepare notes there:

public void addNote(String title, String content, Id linkedTo, String visibility, String shareType) {
    ContentNote cn = new ContentNote();

    if (title != null && title.normalizeSpace().length() > 0) {
        cn.Title = title;
    } else {
        cn.Title = 'Untitled Note';
    }

    cn.Content = Blob.valueOf(content.escapeXML().replace('\r\n', '<br>').replace('\r', '<br>').replace('\n', '<br>').replace('&apos;', '&#39;'));

    // Go on to insert the ContentNote
}

If you don't precisely meet the expectations, you'll get an UnexpectedException, which is uncatchable and unhandleable in the same way as a LimitException.

Existing App

You may also want to look at Doug Ayers' existing application for converting classic Notes into ContentNotes.

Related Topic