different-ai/openwork · error · Error

XLSX workbook.xml was not found.

Error message

XLSX workbook.xml was not found.

What it means

Error thrown while parsing an uploaded XLSX (Excel) attachment: the ZIP archive was opened but the required workbook.xml part is missing from it. This means the file is not a valid XLSX workbook — commonly it is a renamed CSV/XML file, an .xls (legacy binary) file renamed to .xlsx, a corrupted download, or an empty/partial file. Fix by re-exporting the file as a genuine .xlsx from Excel/LibreOffice and re-uploading.

Source

Thrown at apps/server/src/opencode-plugins/openwork-office-attachments.ts:633

      ...(formulaBlock ? { formula: xmlText(formulaBlock.inner) } : {}),
      ...(formulaBlock?.attributes.t ? { formulaType: formulaBlock.attributes.t } : {}),
      ...(formulaBlock?.attributes.ref ? { formulaRef: formulaBlock.attributes.ref } : {}),
      ...(rawValue !== undefined ? { rawValue } : {}),
      ...(displayValue !== undefined ? { displayedValue: displayValue } : {}),
    });
  }
  return { dimension, mergedRanges, cells, omittedCells };
}

function quoted(value: string): string {
  const encoded = JSON.stringify(value.length > 500 ? `${value.slice(0, 500)}…` : value);
  return typeof encoded === "string" ? encoded : "\"\"";
}

function extractXlsxText(bytes: Buffer): string {
  const entries = zipEntryMap(listZipEntries(bytes));
  const workbookXml = readZipTextEntry(bytes, entries, "xl/workbook.xml");
  if (!workbookXml) throw new Error("XLSX workbook.xml was not found.");
  const sharedStrings = parseSharedStrings(readZipTextEntry(bytes, entries, "xl/sharedStrings.xml"));
  const numberFormats = parseXlsxNumberFormats(readZipTextEntry(bytes, entries, "xl/styles.xml"));
  const sheets = parseWorkbookSheets(workbookXml, readZipTextEntry(bytes, entries, "xl/_rels/workbook.xml.rels"));
  if (sheets.length === 0) throw new Error("XLSX workbook contained no sheets.");

  const lines = [
    "xlsx_workbook:",
    `  sheet_count: ${sheets.length}`,
    `  shared_string_count: ${sharedStrings.length}`,
    `  style_count: ${numberFormats.length}`,
    "  sheets:",
  ];
  let remainingCells = MAX_XLSX_CELLS;
  for (const sheet of sheets.slice(0, MAX_XLSX_SHEETS)) {
    lines.push(`  - name: ${quoted(sheet.name)}`);
    lines.push(`    sheet_id: ${quoted(sheet.sheetId)}`);
    if (sheet.relationshipId) lines.push(`    relationship_id: ${quoted(sheet.relationshipId)}`);
    lines.push(`    path: ${quoted(sheet.path)}`);

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Open the file in Excel/LibreOffice and re-save it as a proper .xlsx, then retry.
  2. Verify the zip contains xl/workbook.xml at the root-relative path (unzip -l file.xlsx | grep workbook.xml).
  3. Ensure the upload is a real xlsx, not a renamed xls/csv/zip.
  4. Check the file isn't password-protected/encrypted.
  5. Re-create the archive preserving the original folder structure (OOXML root must contain [Content_Types].xml, xl/, etc.).

Example fix

// before: trusting the extension
if (file.name.endsWith(".xlsx")) return extractXlsxText(bytes);
// after: verify structure first
if (file.name.endsWith(".xlsx")) {
  if (!zipHasEntry(bytes, "xl/workbook.xml")) throw new ApiError(400, "invalid_xlsx", "Not a valid XLSX workbook");
  return extractXlsxText(bytes);
}
Defensive patterns

Strategy: validation

Validate before calling

import { readFile } from "node:fs/promises";
async function looksLikeXlsx(path: string): Promise<boolean> {
  const buf = await readFile(path);
  return buf.subarray(0, 2).toString("latin1") === "PK" && buf.includes(Buffer.from("xl/workbook.xml"));
}

Type guard

function hasWorkbookEntry(bytes: Buffer): boolean {
  return bytes.includes(Buffer.from("xl/workbook.xml"));
}

Try / catch

try {
  const text = extractXlsxText(bytes);
} catch (err) {
  if (err instanceof Error && err.message.includes("workbook.xml was not found")) {
    throw new ApiError(400, "invalid_xlsx", "File is not a valid XLSX workbook; please re-save it from Excel.");
  }
  throw err;
}

Prevention

When it happens

Trigger: extractOfficeText("xlsx", bytes) on a file whose zip lacks xl/workbook.xml — an empty or non-Office zip renamed to .xlsx, a badly re-zipped archive with a wrong root folder, or an encrypted workbook.

Common situations: Users renaming .zip/.xls to .xlsx; archives created without the xl/ folder prefix; password-protected workbooks stored encrypted; attachments corrupted so entries were dropped.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/6d5d89eb67da043b. Report an issue: GitHub.