{"record":{"id":"0ecdc69458e297bb","repo":"lovell/sharp","slug":"recursive-join-is-unsupported","errorCode":null,"errorMessage":"Recursive join is unsupported","messagePattern":"Recursive join is unsupported","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/input.mjs","lineNumber":99,"sourceCode":"    inputDescriptor.buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);\n  } else if (is.plainObject(input) && !is.defined(inputOptions)) {\n    // Plain Object descriptor, e.g. create\n    inputOptions = input;\n    if (_inputOptionsFromObject(inputOptions)) {\n      // Stream with options\n      inputDescriptor.buffer = [];\n    }\n  } else if (!is.defined(input) && !is.defined(inputOptions) && is.object(containerOptions) && containerOptions.allowStream) {\n    // Stream without options\n    inputDescriptor.buffer = [];\n  } else if (Array.isArray(input)) {\n    if (input.length > 1) {\n      // Join images together\n      if (!this.options.joining) {\n        this.options.joining = true;\n        this.options.join = input.map(i => this._createInputDescriptor(i));\n      } else {\n        throw new Error('Recursive join is unsupported');\n      }\n    } else {\n      throw new Error('Expected at least two images to join');\n    }\n  } else {\n    throw new Error(`Unsupported input '${input}' of type ${typeof input}${\n      is.defined(inputOptions) ? ` when also providing options of type ${typeof inputOptions}` : ''\n    }`);\n  }\n  if (is.object(inputOptions)) {\n    // failOn\n    if (is.defined(inputOptions.failOn)) {\n      if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {\n        inputDescriptor.failOn = inputOptions.failOn;\n      } else {\n        throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);\n      }\n    }","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/lovell/sharp/blob/56676c69180a32b468123e090f87bfee30539b49/lib/input.mjs#L81-L117","documentation":"Thrown when sharp is asked to join (stack/tile) an array of images while a join is already in progress on the same instance (this.options.joining is already true). Sharp sets a 'joining' flag when it begins processing an array input and recursively builds input descriptors for each element; if one of those elements is itself an array that triggers another join, the nested join cannot be represented and is rejected.","triggerScenarios":"Passing a nested array as input to sharp: sharp([[imgA, imgB], [imgC, imgD]]). Also when reusing a sharp instance that is mid-join, or building join inputs where one element resolves to another array via _createInputDescriptor. The flag this.options.joining being truthy on re-entry is the exact trigger.","commonSituations":"Attempted grid/tile joins where the developer passes a 2D array hoping for rows-and-columns layout (sharp only supports flat 1D joins). Dynamically composing join lists where flattening was forgotten.","solutions":["Flatten the input array to a single level before passing it: sharp(images.flat()).","Restructure so each join is its own sharp instance rather than nesting arrays in one call.","For grid layouts, join row-by-row into intermediate buffers, then join the row results in a second pass."],"exampleFix":"// before\nsharp([[a, b], [c, d]], { join: { acro... } })\n\n// after\nsharp([a, b, c, d].flat(), { join: { ... } })","handlingStrategy":"validation","validationCode":"function flattenJoinInput(maybeNested) {\n  if (!Array.isArray(maybeNested)) return maybeNested;\n  const flat = maybeNested.flat(Infinity);\n  if (flat.length < 2) throw new Error('Join requires at least two images');\n  return flat;\n}\nsharp(flattenJoinInput(maybeNested), { join: {} });","typeGuard":"function isFlatImageArray(input) {\n  return Array.isArray(input) && input.length >= 2 && input.every(i => !Array.isArray(i));\n}","tryCatchPattern":null,"preventionTips":["Always flatten nested arrays before passing them as join input.","Keep join inputs as a single-level list built by a helper.","Do not reuse a sharp instance that is already joining."],"tags":["join","input","nested-array","validation"],"backgroundTag":null,"analyzedSha":"56676c69180a32b468123e090f87bfee30539b49","analyzedAt":"2026-08-13T04:44:31.201Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}