Troubleshooting
This page catalogs platform-specific behaviors, error codes, and API quirks that differ from SQL Server and general expectations. Each entry follows the pattern: symptom (what you see), cause (why it happens), fix (what to do). For a quick-lookup table version, see the Gotchas Quick Reference.
SQL & Data
Case-sensitive quoted identifiers
Symptom: Query works locally (DuckDB) but fails in Fabric with a column-not-found error.
Cause: Fabric Warehouse IS case-sensitive for quoted column identifiers. "LF_loading_time" cannot be referenced as lf_loading_time. DuckDB is case-insensitive, so this is invisible during local development.
Fix: Use bracket notation [LF_loading_time] or a normalizing CTE that aliases columns to consistent casing.
datetime2 precision required
Symptom: Error 24597 when creating or inserting into a table with datetime2 columns.
Cause: Fabric requires explicit precision: datetime2(6). Bare datetime2 without precision is not accepted.
Fix: Always specify precision explicitly: datetime2(6).
Implicit transaction rollback
Symptom: A script partially succeeds then all changes disappear, including statements that ran before the error.
Cause: Fabric Warehouse rolls back the ENTIRE transaction on ANY error (including 15151). SQL Server does not do this — it allows partial commits. This means a single failing statement undoes everything.
Fix: Use autocommit=True in pyodbc for scripts that mix DDL with potentially-failing statements.
Misleading error code 15151
Symptom: Error SQLSTATE 42000 + error 15151 for what appears to be a missing table.
Cause: Fabric returns 15151 for both "Cannot find the schema" AND "Cannot find the object". SQL Server returns 42S02 + error 208 for missing objects. The error message is ambiguous.
Fix: Check for "15151" in error handling, not "42S02" or "Invalid object name". Verify both schema and object exist.
Bare varchar defaults to varchar(30)
Symptom: Data appears truncated — strings are cut off, GROUP BY produces fewer rows than expected.
Cause: cast(x as varchar) without explicit length defaults to varchar(30) in Fabric/T-SQL, silently truncating data. DuckDB does not truncate, so this is invisible locally.
Fix: Always use explicit lengths: varchar(500). This caused dim_logistics_addresses to lose 130 rows (1050 to 920) because long address strings were truncated and collapsed by GROUP BY.
dbt contract enforcement
Symptom: dbt build fails with contract mismatch even though the query runs fine.
Cause: dbt contracts compare column names EXACTLY against SELECT output. Quoted aliases like "COM-AGE" must match the contract name: field character-for-character.
Fix: Ensure aliases in SELECT match the contract definitions exactly.
Git Sync
PreferRemote removes everything
Symptom: After connecting a workspace to git with PreferRemote, all existing workspace items (including warehouses) are gone.
Cause: PreferRemote initialization removes ALL workspace items not represented in the git folder. This is why Gold (Warehouse) must never be git-synced.
Fix: Only use PreferRemote on empty workspaces or workspaces where git is the sole source of truth. Lakehouse must have .platform files pre-committed.
Git sync always imports
Symptom: Items you didn't expect to change were modified after git sync.
Cause: Git sync ALWAYS imports items from git regardless of initialization_strategy. The strategy only controls how conflicts are resolved, not whether import happens.
Fix: Understand that git sync is a one-way import. Changes in the workspace that conflict with git will be overwritten.
Disconnect doesn't clean up
Symptom: After disconnecting a workspace from git, old synced items remain.
Cause: POST /v1/workspaces/\{id\}/git/disconnect does NOT remove previously synced items. It only breaks the connection.
Fix: Must explicitly DELETE all workspace items before reconnecting if you want a clean slate.
Report GUID mismatch (PowerBIEntityNotFound)
Symptom: Report fails to deploy with PowerBIEntityNotFound error.
Cause: .pbir files embed workspace-specific semantic model GUIDs. Deploying to a different environment without GUID substitution fails because the GUID doesn't exist there.
Fix: Use fabric-cicd parameter files (deployment/parameter-ENV.yml) for find/replace of environment-specific GUIDs. See Two-Pass Deployment.
Lakehouse requires 3 files
Symptom: MissingItemDefinitionFiles error when syncing a Lakehouse.
Cause: A Lakehouse requires .platform, lakehouse.metadata.json, and shortcuts.metadata.json. Missing any one causes this error.
Fix: Ensure all three files are committed to git for any Lakehouse that participates in git sync.
Auth & SPNs
REST API git connect fails with SPN
Symptom: PrincipalTypeNotSupported when calling /git/connect with an SPN.
Cause: Fabric REST API /git/connect does NOT work with SPNs. Only the Terraform fabric_workspace_git resource works.
Fix: Always use Terraform for git connections, never the REST API directly.
ODBC SPN auth broken in CI
Symptom: ODBC Driver 18 ActiveDirectoryServicePrincipal auth times out on Azure DevOps Ubuntu agents.
Cause: libmsal issue on Ubuntu hosted agents causes reliable timeouts with SPN ODBC auth.
Fix: Use CLI auth (az account get-access-token) instead. All dbt and security script steps must run inside AzureCLI@2 tasks.
SPN auto-admin conflict
Symptom: PrincipalAlreadyHasWorkspaceRolePermissions when assigning Admin to the creating SPN.
Cause: The SPN that creates a workspace automatically gets Admin role. Explicitly re-assigning Admin fails.
Fix: Skip the Admin role assignment for the creating SPN. It already has it.
Object ID vs Client ID
Symptom: Auth works but role assignment fails (or vice versa).
Cause: SPN principal_id in Terraform is the Entra ID OBJECT ID. For token acquisition, use CLIENT ID. For Fabric role assignments, use OBJECT ID. These are different GUIDs.
Fix: Use Object ID for fabric_workspace_role_assignment. Use Client ID for az login --service-principal.
Managed Identity needs tenant setting
Symptom: MI has correct workspace roles but Fabric API calls return 403.
Cause: A Managed Identity cannot call Fabric APIs until added to a security group listed in Fabric Admin Portal > Tenant settings > "Service principals can use Fabric APIs". Group membership propagation takes up to 1 hour.
Fix: Add the MI to the appropriate security group. Wait up to 1 hour for propagation.
Fabric API token scope
Symptom: 401 Unauthorized on Fabric API calls despite valid credentials.
Cause: Using the wrong token scope. https://analysis.windows.net/powerbi/api/.default is the legacy Power BI scope and will 401 on Fabric endpoints.
Fix: Use https://api.fabric.microsoft.com/.default for Fabric REST API. Use https://database.windows.net/.default for pyodbc/TDS Warehouse connections.
TMDL
Description syntax causes parse failure
Symptom: Workload_FailedToParseFile when deploying a semantic model.
Cause: Using description: "text" instead of triple-slash syntax. TMDL uses /// for descriptions, NOT description: key-value.
Fix: Use /// Description text on the line immediately above the object declaration. No blank line between /// and the object.
DAX reserved word errors
Symptom: Failed to resolve name 'SYNTAXERROR' — an opaque error with no hint about the cause.
Cause: Table or column name is a DAX reserved keyword (e.g., refresh, date, time, year).
Fix: Always quote table names with single quotes: 'refresh'[column]. When in doubt, quote.
DirectLake table with only measures fails
Symptom: Semantic model fails to load a table that only contains measures.
Cause: A DirectLake table with ONLY measures and no columns cannot initialize its partition.
Fix: Always include at least one column (e.g., a hidden dummy column matching the warehouse schema).
Reports
Embedded GUIDs cause cross-environment failures
Symptom: Report works in DEV but fails in UAT/PROD.
Cause: .pbir files embed environment-specific semantic model GUIDs in their connectionString.
Fix: Use deployment/parameter-ENV.yml for GUID substitution via fabric-cicd.
pbiModelDatabaseName causes parse failure
Symptom: Workload_FailedToParseFile when deploying a report.
Cause: The .pbir file contains pbiModelDatabaseName, which is not valid for Fabric reports.
Fix: Remove pbiModelDatabaseName from the .pbir file. Only connectionString with semanticmodelid=<GUID> should be present.
Report deploy fails with "not authorized on updateDefinition"
Symptom: fabric-deploy reports stage fails for every existing report with The executing identity is not authorized to call POST on .../updateDefinition, even though the deploy SPN is workspace Admin and the semantic stage in the same run succeeds.
Cause: The item on the server is stuck — most commonly a PBIRLegacy item that can no longer accept a PBIR payload through updateDefinition, or an item whose last-write principal no longer matches the deploy SPN. updateDefinition returns 403 regardless of RBAC. GET /v1/workspaces/{ws}/reports/{id} returning Unauthorized for a workspace Admin is the tell — the Items API can't serve the item. A secondary trigger is feat-environment bindings leaking into main (see Embedded GUIDs entry), which masquerades as the same symptom because Fabric 403s on a deleted semanticmodelid reference.
Fix: Rule out feat-binding contamination first (grep -r 'feat-' workspaces/reports/ workspaces/semantic/). If source is clean, delete the stuck items via the Power BI v1 REST API (DELETE /v1.0/myorg/groups/{ws}/reports/{id}) and let the next fabric-deploy run recreate them via the create path. Newly created items are owned by the deploy SPN, so future updateDefinition calls work. Safe in DEV; in UAT/PROD prefer investigating ownership before deleting.
Parameter file silently ignored by fabric-cicd
Symptom: fabric-cicd logs Parameter file not found with path: .../workspaces/<ws>/deployment/parameter-<env>.yml as a warning and continues. No find_replace substitutions run, but deploys appear to succeed.
Cause: fabric-cicd v0.3.x resolves a relative parameter_file_path against repository_directory, not against the process CWD. Passing 'deployment/parameter-dev.yml' with repository_directory='workspaces/reports' is joined to workspaces/reports/deployment/parameter-dev.yml — which doesn't exist. Missing file is a warning, not an error, so UAT/PROD deploys can silently ship DEV-bound reports.
Fix: Always pass an absolute path: parameter_file_path=os.path.abspath(os.path.join(repo_root, 'deployment', f'parameter-{deploy_env}.yml')). Verify by looking for Parameter file validation passed in the pipeline log — its absence means substitutions are being skipped.
Stale parameter-dev.yml rewrites live GUIDs to ghost GUIDs
Symptom: After the parameter-file path bug is fixed and substitutions actually run, fabric-deploy starts failing with PowerBIEntityNotFound. Report Workload failed to import the report with report id <guid>. An error occurred in the Entity Framework on most reports, but a couple succeed inexplicably.
Cause: deployment/parameter-dev.yml contains find_replace pairs that map currently-live DEV semantic-model GUIDs to ghost GUIDs from a previous DEV provision. Before the path-bug fix the file was a silent no-op so the staleness was invisible; after the fix every substitution runs and rewrites correct connectionStrings into broken ones. Reports that appear to work are the ones whose GUID either had an identity mapping or wasn't in the file at all. DEV's source .pbir files are already pinned to live DEV GUIDs — no substitution is needed or wanted on DEV.
Fix: Keep parameter-dev.yml restricted to identity-mapped URL/server entries only. Do NOT include semantic_model_id find/replace pairs for DEV. pipelines/infra-deploy.yml:263 already skips regenerating parameter-dev.yml because DEV is the source environment; if the file is ever recreated by an older generator, strip the semantic-model-ID section manually. UAT/PROD parameter files regenerate correctly because their find_value is the DEV GUID and replace_value is queried live from the target workspace.
Azure Functions
No az CLI on Linux Consumption plan
Symptom: Timer function fails with [Errno 2] No such file or directory: 'az'.
Cause: The az CLI is not installed on the Azure Functions Linux Consumption runtime.
Fix: Use DefaultAzureCredential from azure.identity instead of shelling out to az account get-access-token.
App Insights requests table empty
Symptom: Portal Monitor tab is blank, requests KQL table returns no rows.
Cause: host.json setting "Host.Results": "Error" suppresses request telemetry for successful invocations.
Fix: Change to "Information" to see all invocations. As a workaround, use the traces table: traces | where message contains 'Executing' and message contains 'Functions.'.
Cold-start telemetry loss
Symptom: Timer function definitely ran (blob timer status confirms it) but no telemetry appears.
Cause: On Consumption plan, daily timer functions trigger a cold start. App Insights telemetry may be lost if the instance scales down before flushing.
Fix: Verify execution via timer status blobs in Storage Account (azure-webjobs-hosts/timers/). Consider switching to a Premium plan if telemetry completeness is critical.
past_due cascading
Symptom: Function runs repeatedly in quick succession, host restarts frequently.
Cause: If a timer function runs longer than its schedule interval (e.g., 4 min run on 15 min schedule), subsequent invocations fire immediately as "past_due".
Fix: Ensure function execution time is well within the schedule interval. Add timeout guards if needed.
Workspace Layout
Case-sensitive git connections
Symptom: Terraform git connection fails with a cryptic error.
Cause: Fabric's API does exact string comparison on organization_name and project_name. Azure DevOps URLs are case-insensitive but Fabric is not.
Fix: Use exact case: geris-devops for org, insights-requests for project (NOT fabric-platform).
Branch policies block Fabric commits
Symptom: Cannot commit changes from Fabric UI to the workspace's connected branch.
Cause: Branch policies on main block direct commits, including those from Fabric's git sync.
Fix: Use unprotected feature branches for Fabric UI development. Merge to main via PR.
Related Pages
- Gotchas Quick Reference — table format for quick lookup
- Emergency Procedures — recovery procedures
- Semantic Models — TMDL rules and conventions