{"record":{"id":"909362db17cffa43","repo":"remix-run/remix","slug":"unexpected-data-after-end-of-stream","errorCode":null,"errorMessage":"Unexpected data after end of stream","messagePattern":"Unexpected data after end of stream","errorType":"exception","errorClass":"MultipartParseError","httpStatus":null,"severity":"error","filePath":"packages/multipart-parser/src/lib/multipart.ts","lineNumber":292,"sourceCode":"    this.#findOpeningBoundary = createSearch(`--${boundary}`)\n    this.#openingBoundaryLength = 2 + boundary.length // length of '--' + boundary\n    let boundaryPattern = `\\r\\n--${boundary}`\n    this.#findBoundary = createSearch(boundaryPattern)\n    this.#findPartialTailBoundary = createPartialTailSearch(boundaryPattern)\n    this.#boundaryLength = 4 + boundary.length // length of '\\r\\n--' + boundary\n    this.#boundaryBytes = encodeAsciiPattern(boundaryPattern)\n  }\n\n  /**\n   * Write a chunk of data to the parser.\n   *\n   * @param chunk A chunk of data to write to the parser\n   * @yields Parsed {@link MultipartPart} objects that became available from this chunk\n   * @returns A generator yielding `MultipartPart` objects as they are parsed\n   */\n  *write(chunk: Uint8Array): Generator<MultipartPart, void, unknown> {\n    if (this.#state === MultipartParserStateDone) {\n      throw new MultipartParseError('Unexpected data after end of stream')\n    }\n\n    let index = 0\n    let chunkLength = chunk.length\n\n    if (this.#buffer !== null) {\n      if (this.#state === MultipartParserStateBody) {\n        let carry = this.#buffer\n        let carryResult = this.#analyzeCarryBoundary(carry, chunk)\n\n        if (carryResult.kind === 'none') {\n          this.#append(carry)\n        } else if (carryResult.kind === 'partial') {\n          if (carryResult.start > 0) {\n            this.#append(carry.subarray(0, carryResult.start))\n          }\n\n          let tailLength = carry.length + chunk.length - carryResult.start","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/multipart-parser/src/lib/multipart.ts#L274-L310","documentation":"MultipartParser.write() throws MultipartParseError('Unexpected data after end of stream') when more data arrives after the parser has reached its Done state (the closing boundary was already seen). A well-formed multipart message ends at its terminating delimiter, so extra bytes indicate a corrupted or reused stream.","triggerScenarios":"Calling parser.write(chunk) after the closing --boundary-- has been parsed; feeding a concatenated multipart body (two messages glued together) into one parser; reusing a finished parser instance.","commonSituations":"Keep-alive connection bytes leaking into the body parser, upstream proxies concatenating requests, or client bugs that send the multipart body twice. Rarely, reusing a parser across requests.","solutions":["Do not reuse a MultipartParser after it finishes; create a new instance per message","Inspect the raw body for data after the terminating boundary (e.g. log bytes around --boundary--) and fix the client/proxy that appends it","If parsing concatenated messages, split streams per message before feeding the parser"],"exampleFix":"// before\nlet parser = new MultipartParser(boundary)\nfor (let chunk of chunksA) parser.write(chunk)\nfor (let chunk of chunksB) parser.write(chunk) // throws if A already terminated\n\n// after\nlet parserA = new MultipartParser(boundary)\nfor (let chunk of chunksA) parserA.write(chunk)\nlet parserB = new MultipartParser(boundary)\nfor (let chunk of chunksB) parserB.write(chunk)","handlingStrategy":"validation","validationCode":"// one parser per message — never reuse after done\nlet parser = new MultipartParser(boundary)\nfor (let chunk of messageChunks) {\n  if (parser.isDone) break // stop writing once the closing boundary is seen\n  parser.write(chunk)\n}","typeGuard":null,"tryCatchPattern":"try {\n  parser.write(chunk)\n} catch (error) {\n  if (error instanceof MultipartParseError && error.message === 'Unexpected data after end of stream') {\n    // stray bytes after the terminating boundary — log and stop reading\n    return\n  }\n  throw error\n}","preventionTips":["Create a fresh MultipartParser per request/message","Treat trailing data after --boundary-- as a protocol error upstream, not a parser bug"],"tags":["multipart","parser-state","stream-corruption"],"backgroundTag":"multipart-stream-malformed","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}