FAQ & Troubleshooting
Symptom-first answers. Every import error also carries its own where/what/why/how sentence in the console report — start there.
Setup & first run
"I imported the asset without Addressables — does it compile? Why is import locked?"
The asset compiles without Addressables: the Addressables-using code is guarded behind a SHEETFORGE_ADDRESSABLES version-define. Address loading and the AssetRef@Group type do need com.unity.addressables, so the whole pipeline (import · export · push · write-back) is locked until you install it. Each entry point shows an install notice and stops — no partial run.
Install com.unity.addressables via Package Manager. The Getting Started window's Addressables row has an Open Package Manager button, and that window runs normally instead of being blocked by Safe Mode, because the Editor compiles without the package.
If compile errors remain after installing, they come from other code in the project — SheetForge compiles both with and without the package.
"I updated to a newer version and now the project doesn't compile."
A .unitypackage import adds and updates files but never deletes them. So a file this product retired in a later version can linger and reference an API that no longer exists.
At editor load the dependency-free SheetForge.Setup bootstrap detects those known retired paths and offers to delete them, listing every path before touching anything. Approve the dialog and compilation recovers. Because it lives in its own assembly, it keeps working while the main assemblies are failing. To skip the prompt entirely, delete the Assets/SheetForge folder before importing the new package.
What this does not cover is your own code written against a contract that has since been retired. Port that by hand using the Upgrade notes table in CHANGELOG.md in the source repository — the release package does not ship that file — and Getting Started summarizes what it says.
"Create Sheet only shows the built-in templates — where is the skills demo template? / How do I add my own?"
The built-in Create Sheet list ships two templates plus "from scratch": Item example (core types only), and Enum definitions, which lays out an @enum sheet.
Domain templates that need a plugin (such as the skills demo) are registered by that plugin, so they appear only when the plugin is present. Import the Plugin Demo package and its Skill demo template appears. To ship your own, implement ISheetForgeTemplatePlugin — see Plugin Authoring §4.6.
"Where do I start? / A window keeps opening when I open the Editor."
That's the Getting Started window. It opens automatically the first time the Editor loads, and it is the recommended entry point: Addressables status, choosing the active settings asset, importing an example, and running your first import, all in one place.
Turn off the auto-open with the "Show this window when the Editor starts" toggle at the bottom, and reopen it any time from Tools ▸ SheetForge ▸ Getting Started.
"I imported a demo package but nothing happens — there's no settings asset and no addressable group."
Each demo package bundles a pre-configured settings asset, and importing the package auto-activates it — but only when you have no active settings of your own. If you already have one, the Getting Started window opens to suggest switching, rather than silently changing your setup.
Then press ↓ Pull from source in Tools ▸ SheetForge ▸ Data Studio once: that creates the addressable group and per-tab addresses automatically. Flow: import package → (settings auto-activated) → Run Import → Play.
"The demo scene just shows a text message instead of the demo."
The demo loads by Addressables address, and those addresses exist only after one import on your machine (the group asset is a non-committed, self-healing cache). Import the demo package (its settings are auto-activated) and run Run Import once — see Getting Started §5.
(The demos' committed types use the default SheetForge.Generated namespace, so no generatedNamespace setting is needed — re-import regenerates them in place.)
"The plugin demo's first import fails with UnknownAssetGroup 'Scripts'."
The plugin demo has a script column typed List<AssetRef@Scripts>, which needs an Addressables group named Scripts. Addressables groups are per-machine (not committed), so a freshly-imported demo doesn't have it yet.
The demo auto-configures that group on import (PluginDemoAddressableSetup, fired on domain reload and when you open the demo scene), so a normal import just works. If you still see the error, reopen the demo scene (Tools ▸ SheetForge ▸ Open Plugin Demo Scene) to trigger the setup, then re-import.
This applies only to the plugin demo — your own AssetRef@… groups are ones you register yourself.
"Which settings asset is used when I have more than one?"
The active one. Menus, the Data Studio, and imports all use the active settings asset. Choose it in the Getting Started window or in the Data Studio toolbar dropdown (shown only when multiple exist).
With a single settings asset, the first import auto-selects it. The choice is stored per project and per user (an EditorPrefs pointer — no VCS churn), and self-heals if the active asset is deleted.
"I already have a folder of sheets — what's the fastest way to point SheetForge at it?"
Open the Data Studio and drag the folder onto it, or a single .tsv/.csv/.xlsx file.
It offers to create an import settings asset that reads from that folder and make it active, with no manual field entry. If you already have active settings, the dialog says so and offers to switch.
"How do I check that my project is set up correctly / why import won't run?"
Pick ⋯ ▸ Health Check in the Data Studio toolbar. It reports ✓/✗ with a suggested fix for:
- the active settings;
- source reachability — a local folder that exists, or a Google id + service-account key path, with no network call;
- the import baseline;
- generated-code/baked/addressable freshness.
Results go to the Console plus a summary dialog.
"The menus and UI opened in a language I didn't choose."
On a project's first open, SheetForge sets its UI language from your Editor's system language (nine languages map, otherwise English). It never overrides a language you set yourself. Change it any time in Preferences ▸ SheetForge — see Localization.
(Changing the language triggers one short recompile because the menu labels are regenerated.)
"I cloned the repo and my scene references to baked SOs are Missing."
Expected: baked SOs are per-machine caches with per-machine GUIDs. Never reference them directly from scenes — load by address (SheetForgeDatabases.LoadAsync("Tab")). Run an import once to rebuild your local cache.
"My build was aborted with a SheetForge message."
That's the pre-build freshness hook protecting you from shipping an empty/stale cache. Do what the sentence says — press ↓ Pull from source in Tools ▸ SheetForge ▸ Data Studio — and build again.
Import & validation
"Import ran, found errors, and produced nothing at all."
By design: one error ⇒ no output (no partial assembly). The report lists every problem with coordinates and suggested fixes — fix them in one pass and re-import. You never lose work to this; the sheet is untouched.
"Can I jump straight to the cell an error is about?"
Yes. Each error in the human-readable Console report has a clickable "Open in Data Studio" link. Clicking it opens the Data Studio, switches to that tab and highlights that cell (tab-only for file/tab-level errors). The machine-readable coordinate line is unchanged, so CI/log scraping is unaffected.
"Import wrote code, recompiled… did it finish?"
Yes. When a schema is new/changed, import is internally two-stage (codegen → compile/reload → bake), and the bake resumes automatically after the reload. Watch the console for the final report.
If your game code no longer compiles (e.g. after a column rename), the chain safe-aborts with an actionable sentence; fix your code and import again.
"Empty cell error, but I wanted the cell optional."
Unmarked types are required (silent-contamination guard). To make a cell optional:
- declare
float?— type default; - declare
int=1— explicit default; - or use
List<T>, where an empty cell is an empty list.
See Sheet Syntax.
"1.5 imports fine, but 1,5 errors."
Deliberate: numbers are locale-independent — always . decimal. Comma decimals, NaN, and Infinity are blocked at the entrance.
"Import got very slow all of a sudden."
Import time is linear in the size of your data: 50k rows × 20 columns ≈ 628 ms in the editor. It stays linear even when many references break at once, because the nearest-match search is budgeted per field and length-prefiltered (≈ 45 ms at 4,000 broken references, headless).
If an import suddenly takes much longer than that, the size of the sheet is the thing to look at, not the error count.
"Unknown marker / unknown type error with a 'did you mean' hint."
Typos in @marker names, type names, or enum members are errors with nearest-match suggestions — apply the suggestion. Unknown @ on an unregistered type name is also an error (typo safety for RecordId@Tab style references).
Google Sheets
"Google import fails with PERMISSION_DENIED (403)."
The spreadsheet is not shared with the service account's client_email address — the key alone grants nothing. Open the JSON key, copy client_email, and share the sheet with it (Viewer for import, Editor for Push). The full walkthrough is in Google Sheets Setup.
"Push says it requires SheetsApi."
You're in ExportUrl mode, which is read-only (no auth). Any write-back needs SheetsApi mode with a service-account key. See Sources, Export & Push. Creating the service account and key is covered in Google Sheets Setup.
"ExportUrl import fails asking for a gid map."
Required: a gid-less export URL silently returns only the first tab, so the map (tab name → #gid=) is enforced. Or switch to SheetsApi mode, which needs no map.
"Push reported skipped cells."
The pre-send live re-fetch found conflicts (a teammate edited a cell, a row moved/disappeared, a duplicate key). Skipped cells are protection, not failure — the report shows applied/skipped counts. Re-import to reconcile, then Push again.
"I deleted rows locally but they're still in the Google sheet after Push."
Row deletions are never pushed (positional deletes against a live sheet are unsafe) — you get a notice instead. Delete the rows in the sheet, then re-import.
Authoring
"Ctrl+Z isn't undoing my staged change."
Two boundaries:
- a focused text field consumes Ctrl+Z first — click elsewhere, then undo;
- after "Reflect to sheet" succeeds, staging history is cleared, so undo works within the pre-reflect session only.
Post-reflect, edit the sheet (it's canonical).
"Some of my staged edits show an 'isolated' badge and weren't reflected."
The sheet changed externally between staging and reflect in a way that broke those edits' logical address (row's key renamed outside / row deleted / key conflict). They are excluded — not silently lost, not blocking the rest. Discard them individually and re-stage against the new baseline.
"I renamed a column/tab and now my game code doesn't compile."
Expected and disclosed in the confirmation dialog: renames change the generated field/class name. Update your game code; the import chain then completes on the next run. The column's data values were fully preserved.
"Can I swap two tab names (A↔B), or rename tabs in a cycle, in one batch?"
Yes. Mutual swaps and cycles (A→B→C→A) stage and reflect in a single batch, and the UI only rejects a real conflict: two renames targeting the same name. References follow the data and are rewritten atomically.
One edge case remains on Google: two swapped tabs that reference each other aren't repointed (local is fully correct). Route the mutual reference through a third tab, or reflect via an intermediate name. See Data Studio and Capabilities & Limits.
"My key rename didn't update a reference I typed in the same batch."
Propagation rewrites baseline cells only — never text you just typed (no silent rewriting of fresh input). Pre-flight flags the dangling reference; fix it yourself.
"Reflect refused because of an xlsx tab."
Two known cases:
- xlsx-origin tabs can't be tab-renamed (workbook protection);
- key-rename propagation that would touch an xlsx tab blocks the whole batch (no partial reflection).
Edit the workbook directly, then re-import.
"I edited a baked SO in the inspector and re-import wiped it."
By design — the sheet is the single source of truth and the SO is a cache. The inspector's "test edit" toggle is explicitly temporary. Make real changes through the sheet or the Data Studio.
Export & misc
"Export fails with a schema mismatch."
Your bake is stale relative to a schema change (ExportSchemaMismatch — fingerprint check). Run an import to complete codegen + bake, then Export/Push.
"My exported float says 1 but the sheet had 1.0."
Semantic round-trip: values are preserved exactly; notation normalizes to the shortest round-trip form. Structure (markers, column order, comments, your text) is preserved 100%.
"xlsx import rejected some cells."
The built-in OOXML reader is intentionally minimal. Three things are unsupported:
- formula cells without cached values;
- error cells;
- in-cell tabs/newlines.
Materialize formulas to values; use ; for lists.
"I can't change the editor language right now."
Language changes are locked while an Import/Export/Push runs (the change triggers a menu-file regeneration + short recompile). Wait for the pipeline to finish.
"Parts of my error report are in English even though my language is Korean/Japanese/…"
The report skeleton and why/how sentences are localized. The runtime-interpolated details (the offending value, suggestions) and low-level logs are inline English — the standard localization boundary.
"Where did Tools ▸ SheetForge ▸ … menu items go after cloning?"
The localized menu file is generated (gitignored) — it self-heals on editor load. If labels are in the wrong language, they regenerate on the next language change or editor start.
Related pages
- Capabilities & Limits — the systematic version of these answers
- Getting Started — setup steps referenced above
- Sheet Syntax — notation rules referenced above