outline/outline · error · Error
msg
Error message
msg
What it means
This is a Sequelize custom validator (`TextLength`) attached to the `Comment.data` JSONB column in server/models/Comment.ts:53. It renders the stored ProseMirror document to plain text via `ProsemirrorHelper.toPlainText`, counts Unicode code points with `Array.from(text).length` (so emoji and CJK characters count as one each, not by UTF-16 code unit), and throws the supplied `msg` when the count falls outside `[min, max]`. The active ceiling is `CommentValidation.maxLength = 1000` characters, so the thrown message reads "Comment must be less than 1000 characters". It fires during model validation on any create or update that persists the `data` field.
Source
Thrown at server/models/validators/TextLength.ts:34
msg?: string;
min?: number;
max: number;
}): (target: object, propertyName: string) => void {
return (target: object, propertyName: string) =>
addAttributeOptions(target, propertyName, {
validate: {
validLength(value: ProsemirrorData) {
let text;
try {
text = ProsemirrorHelper.toPlainText(Node.fromJSON(schema, value));
} catch (_err) {
throw new Error("Invalid data");
}
const length = text ? Array.from(text).length : 0;
if (length > max || length < min) {
throw new Error(msg);
}
},
},
});
}
View on GitHub (pinned to 935a44d4c0)
Solutions
- Render the candidate ProseMirror doc to plain text and measure `Array.from(text).length` client-side before submitting; truncate or block the post when it exceeds 1000.
- Split the content across multiple comments or move long content into a Document (DocumentValidation.maxLength is far higher).
- If you control the editor, enforce the limit with the MaxLength ProseMirror extension (shared/editor/extensions/MaxLength.ts) so the UI prevents the over-length state.
- Verify the `data` blob is valid ProseMirror JSON — a malformed node throws the separate "Invalid data" error at TextLength.ts:29 before the length check.
Example fix
// before — submit unvalidated comment data
await client.comments.create({ documentId, data });
// after — measure rendered text length first
import { ProsemirrorHelper } from "@shared/utils/ProsemirrorHelper";
import { Node } from "prosemirror-model";
import { schema } from "@server/editor";
import { CommentValidation } from "@shared/validations";
const text = ProsemirrorHelper.toPlainText(Node.fromJSON(schema, data));
if (Array.from(text).length > CommentValidation.maxLength) {
throw new Error(`Comment must be less than ${CommentValidation.maxLength} characters`);
}
await client.comments.create({ documentId, data }); Defensive patterns
Strategy: validation
Validate before calling
import { CommentValidation } from "@shared/validations";
import { ProsemirrorHelper } from "@shared/utils/ProsemirrorHelper";
import { Node } from "prosemirror-model";
import { schema } from "@server/editor";
/**
* Returns true when the rendered text length fits the Comment limit.
*
* @param data ProseMirror document data.
* @return true if the comment is safe to persist.
*/
function isCommentLengthValid(data: ProsemirrorData): boolean {
const text = ProsemirrorHelper.toPlainText(Node.fromJSON(schema, data));
return Array.from(text).length <= CommentValidation.maxLength;
} Type guard
/**
* Narrows a value to a valid-length Comment data object.
*
* @param value Candidate ProseMirror document data.
* @return true when value parses and is within the length limit.
*/
function isValidCommentData(value: unknown): value is ProsemirrorData {
try {
const node = Node.fromJSON(schema, value as ProsemirrorData);
const text = ProsemirrorHelper.toPlainText(node);
return Array.from(text).length <= CommentValidation.maxLength;
} catch {
return false;
}
} Try / catch
try {
await comment.save();
} catch (err) {
if (err instanceof Sequelize.ValidationError
&& err.errors.some((e) => e.message.startsWith("Comment must be less than"))) {
// truncate or prompt the user to shorten the comment
} else {
throw err;
}
} Prevention
- Measure rendered text length with Array.from(text).length, not string .length, to match the validator's Unicode counting.
- Wire the MaxLength ProseMirror extension into the comment editor so over-length input is blocked at the UI.
- Run client-side validation with the same CommentValidation.maxLength constant shared between client and server.
- Treat 'Invalid data' (a separate throw at TextLength.ts:29) as a malformed-doc bug, not a length problem.
When it happens
Trigger: Creating or updating a Comment (`POST /api/comments.create`, `/api/comments.update`) whose rendered plain text exceeds 1000 Unicode code points. Because the count is on extracted text, a document containing many short nodes (list items, mentions) can hit the limit even when the raw JSON feels small. It also fires when `min > 0` is configured and the text is too short, though the Comment usage leaves `min` at its default of 0.
Common situations: Pasting a large block of text or a long threaded reply into a comment; programmatic imports/bot integrations that submit untruncated bodies; migrations or test fixtures that seed oversized comments; emoji-heavy content where byte-length estimates undercount because the validator measures code points, not bytes.
Related errors
- Sort must be an object with field,direction
- Invalid data
- Invalid subdomain
- Host is not an Outline installation
- The selected file type is not supported (${file.type})
AI-assisted analysis of outline/outline@935a44d4c0 (2026-08-12).
Data as JSON: /api/errors/2a29ad5ece543441.
Report an issue: GitHub.