lovell/sharp · error · Error
Expected input to be an array of images to join
Error message
Expected input to be an array of images to join
What it means
Thrown when inputOptions.join is provided (join configuration) but this.options.join is not set — meaning there is no array of images to apply the join options to. Join options only make sense alongside a join input; supplying them without a join input is a contract violation. The guard is the else of `if (is.defined(this.options.join))`.
Solutions
- Only include inputOptions.join when the input is an array of images: sharp([a, b], { join: { ... } }).
- Conditionally add join options only when images.length >= 2.
- Strip join options for single-image pipelines.
Example fix
// before
sharp(singleBuffer, { join: { animated: true } })
// after
sharp([a, b], { join: { animated: true } }) Defensive patterns
Strategy: validation
Validate before calling
function maybeJoin(input, joinOpts) {
if (joinOpts && !Array.isArray(input)) {
throw new Error('join options require an array input of >= 2 images');
}
const opts = Array.isArray(input) && input.length >= 2 ? { join: joinOpts } : {};
return sharp(input, opts);
} Type guard
function joinOptionsApplicable(input, options) {
return !(options && options.join !== undefined) || (Array.isArray(input) && input.length >= 2);
} Prevention
- Only include join options when the input is an array of images.
- Strip join options for single-image pipelines.
- Build options conditionally based on input type.
When it happens
Trigger: sharp(buffer, { join: { animated: true } }) — passing a single buffer input plus join options. sharp('file.png', { join: { halign: 'center' } }) with no array input. Building options from a template that always includes join even when the input is a single image.
Common situations: Reusing an options object across single-image and multi-image code paths. Misunderstanding that join options belong to the array-input form. Auto-generating options where join was conditionally populated for one case but applied to another.
Related errors
- Expected at least two images to join
- Recursive join is unsupported
- Expected a valid string to create an image with text.
- Expected create.height
- Expected noise to be an object
AI-assisted analysis of lovell/sharp@56676c6918 (2026-08-13).
Data as JSON: /api/errors/107b5fbb75115aee.
Report an issue: GitHub.
Appendix: source
Thrown at lib/input.mjs:515
if (is.defined(inputOptions.join.background)) {
inputDescriptor.joinBackground = this._getBackgroundColourOption(inputOptions.join.background);
}
if (is.defined(inputOptions.join.halign)) {
if (is.string(inputOptions.join.halign) && is.string(this.constructor.align[inputOptions.join.halign])) {
inputDescriptor.joinHalign = this.constructor.align[inputOptions.join.halign];
} else {
throw is.invalidParameterError('join.halign', 'valid alignment', inputOptions.join.halign);
}
}
if (is.defined(inputOptions.join.valign)) {
if (is.string(inputOptions.join.valign) && is.string(this.constructor.align[inputOptions.join.valign])) {
inputDescriptor.joinValign = this.constructor.align[inputOptions.join.valign];
} else {
throw is.invalidParameterError('join.valign', 'valid alignment', inputOptions.join.valign);
}
}
} else {
throw new Error('Expected input to be an array of images to join');
}
}
} else if (is.defined(inputOptions)) {
throw new Error(`Invalid input options ${inputOptions}`);
}
return inputDescriptor;
}
/**
* Handle incoming Buffer chunk on Writable Stream.
* @private
* @param {Buffer} chunk
* @param {string} encoding - unused
* @param {Function} callback
*/
function _write (chunk, _encoding, callback) {
if (Array.isArray(this.options.input.buffer)) {
if (is.buffer(chunk)) {View on GitHub (pinned to 56676c6918)