lovell/sharp · error · Error
Expected both left and top to be set
Error message
Expected both left and top to be set
What it means
Thrown by sharp's composite() when an overlay image specifies a positional offset using only one of the 'left' or 'top' coordinates. The library requires offsets to be a complete (x, y) pair because the native compositor positions the overlay via a single 2D offset; providing only one axis is ambiguous and cannot be rendered. The guard explicitly compares the defined-ness of both fields and rejects any mismatch.
Solutions
- Provide both left and top together: { input: overlay, left: 100, top: 50 }.
- If you only need axis-independent placement, drop both left and top and use gravity instead (e.g., gravity: 'center').
- When offsets are conditional, set or omit them as a pair: const offset = useOffset ? { left: x, top: y } : {} then spread offset into the overlay object.
Example fix
// before
sharp(bg).composite([{ input: overlay, left: 100 }]).png().toBuffer()
// after
sharp(bg).composite([{ input: overlay, left: 100, top: 50 }]).png().toBuffer() Defensive patterns
Strategy: validation
Validate before calling
function validateCompositeOffset(overlay) {
const hasLeft = overlay.left !== undefined;
const hasTop = overlay.top !== undefined;
if (hasLeft !== hasTop) {
throw new Error(`Composite offset requires both left and top; got left=${overlay.left}, top=${overlay.top}`);
}
if (hasLeft && (!Number.isInteger(overlay.left) || !Number.isInteger(overlay.top))) {
throw new Error('left and top must be integers');
}
}
// before: sharp(bg).composite([overlay])
// validate each overlay:
overlays.forEach(validateCompositeOffset); Type guard
function hasCompleteOffset(overlay) {
const l = overlay.left, t = overlay.top;
return (l === undefined && t === undefined) || (Number.isInteger(l) && Number.isInteger(t));
} Prevention
- Always set left and top as a pair, or omit both and use gravity.
- When offsets are conditional, build a partial object and spread it only if both axes are known.
- Centralize overlay construction in a helper that enforces the pair invariant.
When it happens
Trigger: Calling sharp(image).composite([{ input: overlay, left: 100 }]) without a top value, or passing { input: overlay, top: 50 } without left. The check `is.defined(image.top) !== is.defined(image.left)` fires whenever exactly one of the two is provided (including when one is explicitly undefined and the other is a number).
Common situations: Developers copy a partial example or compute only the horizontal offset (e.g., centering horizontally but forgetting vertical). Also occurs when conditionally setting offsets via spread: `{ ...(cond ? { left } : {}) }` leaving the other axis unset. Confusion with the 'gravity' option, which is the alternative anchor-based positioning that needs neither left nor top.
Related errors
- Expected a and b to be arrays of the same length
- Expected a valid string to create an image with text.
- Expected at least two images to join
- Expected create.height
- Expected input to be an array of images to join
AI-assisted analysis of lovell/sharp@56676c6918 (2026-08-13).
Data as JSON: /api/errors/b9e659978520c574.
Report an issue: GitHub.
Appendix: source
Thrown at lib/composite.mjs:180
throw is.invalidParameterError('tile', 'boolean', image.tile);
}
}
if (is.defined(image.left)) {
if (is.integer(image.left) && is.inRange(image.left, -100000000, 100000000)) {
composite.left = image.left;
} else {
throw is.invalidParameterError('left', 'integer between -100000000 and 100000000', image.left);
}
}
if (is.defined(image.top)) {
if (is.integer(image.top) && is.inRange(image.top, -100000000, 100000000)) {
composite.top = image.top;
} else {
throw is.invalidParameterError('top', 'integer between -100000000 and 100000000', image.top);
}
}
if (is.defined(image.top) !== is.defined(image.left)) {
throw new Error('Expected both left and top to be set');
} else {
composite.hasOffset = is.integer(image.top) && is.integer(image.left);
}
if (is.defined(image.gravity)) {
if (is.integer(image.gravity) && is.inRange(image.gravity, 0, 8)) {
composite.gravity = image.gravity;
} else if (is.string(image.gravity) && is.integer(this.constructor.gravity[image.gravity])) {
composite.gravity = this.constructor.gravity[image.gravity];
} else {
throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
}
}
if (is.defined(image.premultiplied)) {
if (is.bool(image.premultiplied)) {
composite.premultiplied = image.premultiplied;
} else {
throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
}View on GitHub (pinned to 56676c6918)