different-ai/openwork · error · Error

Office XML text entries contained no extractable text.

Error message

Office XML text entries contained no extractable text.

What it means

After collecting text from all relevant Office XML parts, if the combined extracted text is empty (every part yielded no text or the budget was exhausted before any chunk), extractOfficeText throws this error. It means the archive was structurally fine but had no extractable textual content.

Source

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

  return lines.join("\n").slice(0, MAX_EXTRACTED_TEXT_CHARS);
}

function extractOfficeText(kind: OfficeKind, bytes: Buffer): string {
  if (kind === "xlsx") return extractXlsxText(bytes);
  const entries = listZipEntries(bytes).filter((entry) => relevantXmlEntry(kind, entry.name)).sort(compareEntryName);
  if (entries.length === 0) throw new Error("No supported Office XML text entries were found.");
  const pieces: string[] = [];
  let remaining = MAX_EXTRACTED_TEXT_CHARS;
  for (const entry of entries) {
    if (remaining <= 0) break;
    const text = xmlText(readZipEntryData(bytes, entry).toString("utf8"));
    if (!text) continue;
    const chunk = text.slice(0, remaining);
    pieces.push(`[${entry.name}]\n${chunk}`);
    remaining -= chunk.length;
  }
  const combined = pieces.join("\n\n").slice(0, MAX_EXTRACTED_TEXT_CHARS);
  if (!combined) throw new Error("Office XML text entries contained no extractable text.");
  return combined;
}

function basePartIds(part: Record<string, unknown>): Record<string, unknown> {
  const result: Record<string, unknown> = {};
  for (const key of ["id", "sessionID", "messageID", "sessionId", "messageId"]) {
    const value = part[key];
    if (typeof value === "string" || typeof value === "number") result[key] = value;
  }
  return result;
}

function normalizedText(part: OfficeFilePart, materialized: MaterializedAttachment | null, extractedText: string, error?: string): string {
  return [
    "OpenWork normalized an Office attachment before sending this request to the model.",
    `filename: ${safeFilename(part.filename, part.kind)}`,
    `canonical_mime: ${part.mime}`,
    `sha256: ${materialized?.sha256 ?? "unavailable"}`,

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Verify the document actually contains text (open it and check).
  2. Check MAX_EXTRACTED_TEXT_CHARS isn't 0 or negative in config.
  3. For image-only documents, use OCR instead of XML extraction.
  4. Ensure document text isn't stored only in embedded objects (e.g., embedded xlsx/charts) that the extractor skips.

Example fix

// before
const MAX_EXTRACTED_TEXT_CHARS = Number(process.env.MAX_TEXT_CHARS ?? 0);
// after
const MAX_EXTRACTED_TEXT_CHARS = Number(process.env.MAX_TEXT_CHARS ?? 200_000);
Defensive patterns

Strategy: try-catch

Validate before calling

function configAllowsExtraction(): boolean {
  return Number(process.env.MAX_TEXT_CHARS ?? 200_000) > 0;
}

Type guard

function hasExtractableText(pieces: string[]): boolean {
  return pieces.some((p) => p.trim().length > 0);
}

Try / catch

try {
  const text = extractOfficeText(kind, bytes);
} catch (err) {
  if (err instanceof Error && err.message.includes("no extractable text")) {
    return ""; // treat as image-only/empty document rather than failing
  }
  throw err;
}

Prevention

When it happens

Trigger: extractOfficeText on docx/pptx whose XML parts contain only whitespace/empty runs, only images/shapes without text, or where remaining char budget was already <= 0 (e.g., MAX_EXTRACTED_TEXT_CHARS configured to 0).

Common situations: Image-only documents (scanned pages); presentations with only pictures; documents whose text lives in unsupported parts; misconfigured MAX_EXTRACTED_TEXT_CHARS = 0.

Related errors


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