RepoPilotOpen in app →

qax-os/excelize

Go language library for reading and writing Microsoft Excel™ (XLAM / XLSM / XLSX / XLTM / XLTX) spreadsheets

Healthy

Healthy across the board

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1w ago
  • 66+ active contributors
  • Distributed ownership (top contributor 22% of recent commits)
Show 3 more →
  • BSD-3-Clause licensed
  • CI configured
  • Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/qax-os/excelize)](https://repopilot.app/r/qax-os/excelize)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/qax-os/excelize on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: qax-os/excelize

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/qax-os/excelize shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 1w ago
  • 66+ active contributors
  • Distributed ownership (top contributor 22% of recent commits)
  • BSD-3-Clause licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live qax-os/excelize repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/qax-os/excelize.

What it runs against: a local clone of qax-os/excelize — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in qax-os/excelize | Confirms the artifact applies here, not a fork | | 2 | License is still BSD-3-Clause | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 39 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>qax-os/excelize</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of qax-os/excelize. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/qax-os/excelize.git
#   cd excelize
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of qax-os/excelize and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "qax-os/excelize(\\.git)?\\b" \\
  && ok "origin remote is qax-os/excelize" \\
  || miss "origin remote is not qax-os/excelize (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(BSD-3-Clause)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"BSD-3-Clause\"" package.json 2>/dev/null) \\
  && ok "license is BSD-3-Clause" \\
  || miss "license drift — was BSD-3-Clause at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "excelize.go" \\
  && ok "excelize.go" \\
  || miss "missing critical file: excelize.go"
test -f "file.go" \\
  && ok "file.go" \\
  || miss "missing critical file: file.go"
test -f "sheet.go" \\
  && ok "sheet.go" \\
  || miss "missing critical file: sheet.go"
test -f "cell.go" \\
  && ok "cell.go" \\
  || miss "missing critical file: cell.go"
test -f "styles.go" \\
  && ok "styles.go" \\
  || miss "missing critical file: styles.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 39 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~9d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/qax-os/excelize"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Excelize is a pure Go library for reading, writing, and manipulating Microsoft Excel files (XLSX, XLSM, XLAM, XLTM, XLTX formats) compatible with Excel 2007+. It provides both standard and streaming APIs to handle spreadsheets with complex formatting, formulas, charts, pivot tables, and images without external dependencies. Flat monolithic structure: core API in excelize.go and file.go, feature modules (cell.go, sheet.go via adjust.go, chart.go, picture.go, merge.go, etc.) at root level, each with paired _test.go files. Utility modules for crypto, number formatting, and date handling. All tests run via standard Go test framework; CI defined in .github/workflows/go.yml.

👥Who it's for

Go backend developers and data engineers who need to programmatically generate reports, export data, or parse Excel files in production systems. Common use cases: financial reporting systems, bulk data imports/exports, and automated spreadsheet generation.

🌱Maturity & risk

Production-ready and actively maintained. The project has extensive test coverage (matching *_test.go files alongside all major modules), GitHub Actions CI/CD pipeline (.github/workflows/go.yml), and requires Go 1.25.0+. Recent commits visible in dependencies (e.g., xuri/nfp with 2025 updates) indicate active development.

Low-to-moderate risk for standard use cases. Dependencies are minimal and stable (richardlehane/mscfb for OLE2 parsing, xuri/nfp for number formatting). Single-maintainer risk exists (xuri maintains core), but community contributions are evident. No major breaking changes implied in go.mod versioning (v2 stable).

Active areas of work

Active maintenance on Excel compatibility and formula support (calc.go, calcchain.go recently tested). Recent dependency updates in go.mod (xuri/nfp updated to 2025-05-30 commit). Community-driven via CONTRIBUTING.md and issue templates in .github/ISSUE_TEMPLATE/. Streaming API and complex component support appear to be ongoing focus areas.

🚀Get running

git clone https://github.com/qax-os/excelize.git
cd excelize
go mod download
go test ./...

Daily commands: This is a library, not an application. Run tests: go test -v ./... or go test -cover ./... for coverage. See excelize_test.go for integration test examples. No server/CLI startup required; use in your own Go programs via import "github.com/xuri/excelize/v2".

🗺️Map of the codebase

  • excelize.go — Core entry point and main File struct that all operations depend on; defines the primary API surface.
  • file.go — Handles file I/O, ZIP reading/writing, and XML marshaling/unmarshaling—the lowest-level persistence layer.
  • sheet.go — Core sheet management including cell access, row/column operations, and sheet lifecycle; heavily called by all features.
  • cell.go — Cell-level operations including value setting, reading, and type conversions; fundamental to all data manipulation.
  • styles.go — Style and formatting engine; manages fonts, fills, borders, number formats, and all visual properties.
  • workbook.go — Workbook-level metadata, relationships, and content types management; orchestrates multi-sheet persistence.
  • rows.go — Row iteration and bulk row operations including inserting, deleting, and streaming; critical for batch workflows.

🛠️How to make changes

Add a new style property (e.g., underline style)

  1. Add the property to the Style struct and its JSON tags in styles.go (styles.go)
  2. Update the NewStyle function to parse and validate the new property (styles.go)
  3. Modify the marshaling logic in newStyleXML or similar to include the property in the generated XML (styles.go)
  4. Add tests covering the new property in styles_test.go (styles_test.go)

Add a new cell read/write operation

  1. Define the public API function in cell.go (e.g., GetCellComments, SetCellComments) (cell.go)
  2. Implement the logic to mutate the in-memory worksheet structure (cellData, comments map, etc.) (cell.go)
  3. If persisting to XML is needed, update the marshaling in workbook.go or sheet.go (sheet.go)
  4. Add unit tests in cell_test.go verifying the operation and round-trip Save/Open (cell_test.go)

Add support for a new Excel feature (e.g., conditional formatting rule type)

  1. Create or extend the XML schema struct in xmlCore.go, xmlDrawing.go, or a new xmlFeature.go file (xmlCore.go)
  2. Add a public API function in a domain file (e.g., styles.go or a new feature.go) to construct the rule (styles.go)
  3. Integrate the struct into the workbook marshaling logic in workbook.go or sheet.go (workbook.go)
  4. Add comprehensive tests in the corresponding _test.go file, validating XML output and Excel compatibility (styles_test.go)

Optimize cell/row performance (streaming or caching)

  1. Review the current workbook memory model in excelize.go and sheet.go to understand cell storage (sheet.go)
  2. Implement lazy-loading or streaming logic in rows.go, possibly by introducing a custom Reader interface (rows.go)
  3. Update file.go and excelize.go to manage lifecycle of any new streaming resources (e.g., closing iterators) (file.go)
  4. Add benchmarks and tests in rows_test.go and excelize_test.go to validate memory efficiency (rows_test.go)

🔧Why these technologies

  • Pure Go (no C bindings) — Cross-platform compatibility, single binary deployment, no CGO overhead or external dependencies.
  • ZIP-based file format (XLSX/XLSM/XLAM) — Excel files are ZIP archives containing XML; Go's archive/zip and encoding/xml handle this natively and efficiently.
  • In-memory workbook model — Enables fast random access to cells and styles; simpler API for sequential operations and full file editing.
  • golang.org/x/crypto for encryption — Standard library support for AES and SHA1-based encryption required by older Excel formats.
  • github.com/richardlehane/mscfb for OLE/CFB — Legacy XLS and encrypted file support requires OLE (Compound File Binary) format parsing.

⚖️Trade-offs already made

  • In-memory workbook model vs. streaming

    • Why: Simpler API, random access, and easier feature implementation; enables complex operations like formulas and pivot tables.
    • Consequence: High memory usage for large files (millions of cells); unsuitable for single-pass processing of huge datasets. Mitigated by stream.go for write-only scenarios.
  • Tight XML schema coupling (separate xmlXYZ.go files)

    • Why: Precise control over Excel's complex, nested XML structures; enables round-trip fidelity.
    • Consequence: Many XML struct definitions; difficult to add new properties without understanding schema; risk of breaking compatibility.
  • No formula evaluation engine (calc.go is minimal)

    • Why: Excel formulas are Turing-complete; implementing a full engine would be massive, slow, and diverge from Excel's behavior.
    • Consequence: Excelize reads/writes formulas as strings; users cannot modify or compute formula results in-process. External tools (e.g., LibreOffice, Excel) must evaluate.
  • Mutable File struct with global workbook state

    • Why: Simplifies the API (no builder patterns); natural fit for sequential sheet/cell modifications.
    • Consequence: Not goroutine-safe; callers must serialize access. Harder to parallelize multi-sheet operations.

🚫Non-goals (don't propose these)

  • Does not evaluate Excel formulas; formulas are stored and passed through as-is.
  • Does not support real-time multi-user editing or conflict resolution.
  • Does not provide a graphical UI; library-only, no built-in workbook viewer.
  • Does not implement VBA macro execution; VBA code is preserved but not run.
  • Does not support all legacy XLS-only features (some pivot table styles, some chart types may degrade).

🪤Traps & gotchas

  1. Version import path: Must use github.com/xuri/excelize/v2 in imports (not v1), as declared in go.mod. 2. File format complexity: Excel OLE2 container format (handled by mscfb) has quirks—row/column limits (1048576 rows, 16384 columns) and internal XML structure changes per feature. 3. Memory usage: No explicit streaming for read operations in basic API (GetRows, GetCellValue load full sheets); use streaming APIs for huge datasets. 4. Formula evaluation: calc.go may not evaluate all Excel functions; some formulas require Excel itself. 5. Concurrency: File struct is not goroutine-safe; synchronization needed for multi-threaded access.

🏗️Architecture

💡Concepts to learn

  • Office Open XML (OOXML) — Core format standard (ECMA-376) that Excelize implements; understanding the ZIP-based XML structure and relationships is essential for debugging formatting, formulas, or chart issues
  • OLE2 (Compound Document Binary Format) — XLSM and XLAM files use OLE2 container format for macro storage; Excelize relies on richardlehane/mscfb to parse this binary format
  • Excel Serial Date Number — Excel stores dates as floating-point serial numbers (days since 1900 or 1904); date.go implements conversion logic with off-by-one quirks ('leap year bug')
  • Number Format Code Strings — Excel number formatting uses DSL codes (e.g., '0.00', 'mm/dd/yyyy'); numfmt.go parses and maps these to Go's time.Layout and numeric format patterns
  • Streaming / Lazy Loading Patterns — Excelize offers streaming APIs to avoid loading entire sheets into memory; critical for processing files with millions of rows, implemented via sequential XML parsing
  • Formula Calculation Chain — calcchain.go manages Excel's internal calculation dependency graph; understanding formula order and circular reference detection is needed for formula support
  • XML Namespace Handling in Go — OOXML uses multiple XML namespaces (spreadsheetml, relationships, etc.); Go's encoding/xml package with struct tags is central to marshaling/unmarshaling sheet data
  • tealeg/xlsx — Alternative Go library for XLSX, slower but simpler; comparison reference for feature parity and performance trade-offs
  • juju/gnumeric — Go library for Gnumeric spreadsheets; complementary if you need multi-format spreadsheet support
  • xuri/efp — Direct dependency (Excel formula parser by same author); used internally for formula handling, relevant for understanding formula evaluation
  • xuri/nfp — Direct dependency (number format parser by same author); critical for number/date formatting logic, maintained in lockstep
  • richardlehane/mscfb — Direct dependency for OLE2 container parsing; necessary to understand how XLSM/XLAM binary structure is decoded

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive integration tests for stream.go large file handling

The stream.go file handles streaming writes for large Excel files, but stream_test.go likely has limited coverage for edge cases like memory efficiency, concurrent writes, and file corruption recovery. Adding benchmarks and stress tests would help prevent performance regressions and ensure reliability for users processing multi-GB spreadsheets.

  • [ ] Review stream_test.go coverage against stream.go exported functions (WriteSheet, Flush, etc.)
  • [ ] Add benchmark tests for writing 100K+ row datasets to measure memory allocation patterns
  • [ ] Add tests for concurrent stream writes to multiple sheets and error handling during mid-stream failures
  • [ ] Add tests validating output file integrity after interrupted stream operations

Add missing formula validation tests in calc_test.go for xlsx formula compatibility

The calc.go file handles formula parsing and calculation chains (calcchain.go), but the test coverage doesn't appear to validate formula compatibility with Excel's native formula engine across different locales and function signatures. This is critical since formula errors silently corrupt spreadsheets.

  • [ ] Create test cases in calc_test.go matching known Excel formula edge cases (division by zero, circular references, locale-specific functions like SUMPRODUCT)
  • [ ] Add tests validating that formulas written via SetCellFormula() produce identical results in Excel and Go's calculation engine
  • [ ] Cross-reference calcchain_test.go to ensure formula chain dependencies are correctly validated
  • [ ] Add tests for unsupported Excel functions to ensure graceful error messages rather than silent failures

Add end-to-end round-trip tests for chart.go with all supported chart types

The chart.go file supports multiple Excel chart types (bar, line, pie, scatter, surface, etc.), but chart_test.go likely lacks comprehensive round-trip tests (create → save → read → validate). Missing tests for chart properties like data labels, legends, and axis formatting could lead to chart corruption when reading/writing.

  • [ ] Audit chart_test.go for missing chart types (check against chart.go type definitions like ChartLine, ChartBar, ChartScatter, etc.)
  • [ ] Add round-trip tests: create chart with AddChart() → SaveAs() → OpenFile() → GetChart() → validate properties match
  • [ ] Add tests for chart-specific features: data labels, trend lines, error bars, and legend positioning
  • [ ] Add regression tests for known Excel chart format issues (e.g., chart axis scaling, data point colors persisting correctly)

🌿Good first issues

  • Add missing test coverage for drawing.go (image/shape embedding)—file exists with no visible test file; write tests for AddPicture, AddDrawing operations
  • Extend date.go test coverage—date parsing and Excel date serial number conversion edge cases (pre-1900 dates, leap year bugs) likely need more test scenarios
  • Improve error messages in errors.go—wrap and contextualize low-level XML/ZIP errors with business-level context (e.g., 'failed to parse sheet 'Sheet2' row 500' instead of generic XML unmarshaling errors)

Top contributors

Click to expand

📝Recent commits

Click to expand
  • d4466b6 — Support use formula in chart title (#2288) (AdamDrewsTR)
  • d20d037 — Reduced memory usage for reading unencrypted workbook (#2293) (guangxuewu)
  • 1a1c74a — Fix missing single quote after adjust formula which includes external references (#2301) (rootsec1)
  • 6c1f7ea — Auto foreground and background color when creating solid fill style when no custom color value specified (#2306) (noahchiu22)
  • 873b3d1 — This closes #2299, fix panic on read corrupted workbook contains invalid worksheet XML by column iterator (#2300) (SAY-5)
  • 925e798 — This closes #2297, fix custom number format with comma scaling not properly applied (xuri)
  • 94f8a47 — Prevent panic on add chart with no fill series (#2294) (lawrence3699)
  • 4de2989 — This closes #2289, fix AddComment assigning wrong authorID when author already exists (#2291) (narasaka)
  • 827094a — Correct FREQUENCY output and simplify implementation (#2286) (shcabin)
  • 2694a36 — Update dependencies module to fix vulnerabilities (#271) (martskins)

🔒Security observations

The excelize library has a reasonably secure foundation with use of well-maintained dependencies and no obvious hardcoded secrets. However, there are medium-severity concerns around XML parsing (potential XXE), file handling validation, and path traversal risks inherent to processing untrusted Excel files. The go version specified (1.25.0) appears incorrect and should be verified. Primary recommendations: (1) Audit XML parsing for XXE protections, (2) Implement input validation and file size limits, (3) Validate all extracted file paths, (4) Keep dependencies updated and monitor security advisories.

  • Medium · Outdated Go Version in go.mod — go.mod. The go.mod specifies 'go 1.25.0', which appears to be a future/invalid version. Current stable Go versions are in the 1.21-1.23 range. This may indicate a misconfiguration or typo that could cause compatibility issues. Fix: Update go.mod to specify a supported Go version (e.g., 'go 1.21' or 'go 1.22'). Verify against golang.org releases and update to the minimum supported version appropriate for the project.
  • Medium · Potential XXE (XML External Entity) Vulnerability — file.go, excelize.go, and XML parsing functions. The excelize library reads and parses Excel files (XLSX format), which are ZIP archives containing XML. Without proper XML parsing configuration, XXE attacks could be possible if the library doesn't disable external entity processing. This is a common risk in XML-based document parsers. Fix: Ensure all XML parsing uses safe configurations: disable DTD processing, disable external entity resolution, and validate input file structure. Review golang.org/x/net/html and encoding/xml usage patterns for XXE protections.
  • Medium · Dependency on golang.org/x/crypto with Potential Issues — go.mod - golang.org/x/crypto v0.50.0. The project depends on 'golang.org/x/crypto v0.50.0'. While this is generally a trusted package, older versions may contain known vulnerabilities. The current version should be verified against security advisories. Fix: Run 'go list -u -m all' and check for security advisories using 'go vuln' or GitHub's dependency scanning. Consider updating to the latest available version of golang.org/x/crypto.
  • Low · Missing Input Validation in File Processing — file.go, stream.go, and general file reading functions. Excel files can contain arbitrary content. The library should validate file structure, size limits, and content before processing to prevent DoS attacks through maliciously crafted files (e.g., zip bombs, extremely large sheets). Fix: Implement file size limits, validate ZIP archive structure before extraction, implement streaming limits, and add checks for suspicious file patterns. Document safe handling practices.
  • Low · Potential Path Traversal in File Operations — file.go and ZIP extraction functions (likely using golang.org/x/archive/zip or similar). Excel files contain internal file paths (e.g., worksheets/sheet1.xml). If file extraction doesn't properly validate paths, zip slip attacks could occur where malicious files extract outside intended directories. Fix: Sanitize all file paths extracted from ZIP archives. Use filepath.Clean() and verify paths don't escape the intended extraction directory. Implement allow-list validation for expected internal paths.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · qax-os/excelize — RepoPilot