overleaf/overleaf · error · Error
Adjacent skip components should be combined
Error message
Adjacent skip components should be combined
What it means
text-tp2 requires that consecutive numeric (skip) components be merged into a single number: [3, 4] must be [7]. checkOp tracks the previous component (`last`) and throws if it sees a skip immediately following another skip. Normalized ops from this library never contain adjacent skips, so finding them means the op was hand-built or altered.
Source
Thrown at services/document-updater/app/js/sharejs/types/text-tp2.js:102
) {
throw new Error('Inserts must insert a string or a +ive number')
}
} else if (c.d !== undefined) {
if (typeof c.d !== 'number' || !(c.d > 0)) {
throw new Error('Deletes must be a +ive number')
}
} else {
throw new Error('Operation component must define .i or .d')
}
} else {
if (typeof c !== 'number') {
throw new Error('Op components must be objects or numbers')
}
if (!(c > 0)) {
throw new Error('Skip components must be a positive number')
}
if (typeof last === 'number') {
throw new Error('Adjacent skip components should be combined')
}
}
result.push((last = c))
}
return result
})()
}
// Take the next part from the specified position in a document snapshot.
// position = {index, offset}. It will be updated.
type._takeDoc = takeDoc = function (
doc,
position,
maxlength,
tombsIndivisible
) {
if (position.index >= doc.data.length) {View on GitHub (pinned to 28ad3b03b7)
Solutions
- Sum adjacent numeric components into one before submitting the op (normalize the op).
- When appending a skip, check if the previous component is a number and add to it instead of pushing.
- Only interleave skips with insert/delete components; never emit two skips in a row.
Example fix
// before op.push(3) op.push(4) // after const last = op[op.length - 1] if (typeof last === 'number') op[op.length - 1] = last + 4 else op.push(4)
Defensive patterns
Strategy: validation
Validate before calling
function isNormalized(op) {
for (let i = 1; i < op.length; i++) {
if (typeof op[i] === 'number' && typeof op[i - 1] === 'number') return false
}
return true
}
if (!isNormalized(op)) op = mergeAdjacentSkips(op) Type guard
function hasAdjacentSkips(op) {
return op.some((c, i) => i > 0 && typeof c === 'number' && typeof op[i - 1] === 'number')
} Try / catch
try {
tp2type.apply(doc, op)
} catch (e) {
if (e.message === 'Adjacent skip components should be combined') {
tp2type.apply(doc, mergeAdjacentSkips(op))
} else throw e
} Prevention
- When appending a skip, always try to merge into the previous numeric component first.
- Run ops through a normalize step before persisting or sending.
- Never concatenate two ops arrays directly; rebuild with merging.
When it happens
Trigger: Pushing two numeric components back-to-back into an op array before calling type.apply / type.transform / type.prune; concatenating two ops without normalization; diffing algorithms that emit a skip per unchanged run instead of one merged skip.
Common situations: Hand-rolled diff/merge tools producing unnormalized ops; code that appends to an op whose last component is already a skip without checking; porting ops from other OT types into tp2; middleware that rewrites ops in transit.
Related errors
- Skip components must be a positive number
- Remaining fragments in the op: ${component}
- Referenced element not a string
- Deleted string does not match
- invalid / missing instruction in op
AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03).
Data as JSON: /api/errors/5c5c05cc0b50b534.
Report an issue: GitHub.