Symptom โ Root Cause โ Fix. Every common production issue, organised by what you see first. Search by symptom or browse by category. Built from real implementation war stories.
setDataCellValues() calls targeting non-existent blocks silently fail โ no error thrown. New fiscal year, new scenario, or new version has never had data written to it, so no blocks exist at those sparse intersections.DATACOPY FY2025 -> FY2026 (calc script, copies blocks + data) or a @CREATEBLOCK FIX for zero-based seeding. Check block count in Application โ Statistics before and after.beforeSave Groovy rule threw an unhandled exception, or a throwVetoException() was triggered by a validation guard. Less likely: valid intersection violation blocking the write. Rare: planning unit is Locked or Approved.beforeSave rule and retry. If save works, the rule is throwing. Isolate with a minimal test.Unclassified_Actuals instead of correct EPBCS accounts. 3. POV mismatch โ report shows different Scenario/Version than what the DI rule targeted.runDataRule complete before runBusinessRule "CALC ALL" was called? Verify timestamps โ calc must run after load, not in parallel.ACCUMULATE (Merge) export mode instead of REPLACE. The load added new values on top of existing values. Second most common: the DI rule was run twice โ once manually and once by the nightly automation.REPLACE for all actuals loads. 2. Re-run the DI rule with REPLACE โ it will clear the intersection and reload correctly. 3. Check job history to confirm the rule wasn't run twice. For prevention: REPLACE is mandatory for actuals; ACCUMULATE is only for additive scenarios (budget adjustments).NOCOMMIT export mode first to validate โ if the row counts look correct, then re-run with COMMIT.Unclassified_Actuals catch-all account (if one exists). Without a catch-all, records are silently dropped at Validate step.Unclassified_Actuals account: check its balance. 4. Re-run with REPLACE mode to reload the period correctly. For prevention: add a catch-all Like rule as the last mapping rule so no record is ever silently dropped.BIP Report to ESS Job in DI Application Options. Create an Oracle Enterprise Scheduler Job in Fusion wrapping the same BIP report. ESS runs asynchronously โ DI polls for up to 6.5 minutes. For extracts exceeding even that: partition by ledger or period range into multiple integrations.runDataRule command works but Quick Mode (Direct Load) is not being used โ load is slow.runDataRule does NOT support Quick Mode. Quick Mode requires the EPM REST API's runIntegration endpoint with importMode: "DIRECT". Calling runDataRule always uses the full workflow regardless of what's configured in the UI.POST /rest/v3/applications/{app}/jobs with jobType: "IMPORT_DATA" and importMode: "DIRECT". Note: Quick Mode disables Workbench audit data and drill-through. Only use for high-volume loads where audit is not required.JAVA_HOME to the Java 17 installation. Test epmautomate login in a non-production environment first. Verify no other applications on the server require Java 8 specifically before upgrading.setDataCellValues() silently fails on non-existent blocks. No exception thrown. 2. getCell() returned null (block missing or #MISSING) and null propagated into the write list โ null values written are treated as no-ops.DATACOPY or @CREATEBLOCK calc script before the Groovy rule. 2. Add null-guards: def val = getCell(...) ?: 0 to every getCell() call. 3. Add println("Writing ${writes.size()} cells") before the write call โ if it logs 0, the loop built an empty list.NullPointerException at runtime โ rule aborts, nothing is calculated.getCell() returns null when the intersection is #MISSING or when the block doesn't exist. If the result is used directly in arithmetic without a null-guard, Java/Groovy throws NPE when unboxing null to a primitive.getCell() call: def val = getCell(...) ?: 0. For allocation denominators: guard against zero division separately โ if (totalRev == 0) { println("No revenue"); return }.getCell() / setCell() calls inside a loop โ each is a network round-trip to Essbase. 200 cost centres ร 2 calls = 400 round-trips. At 150ms each = 60 seconds minimum, plus overhead.DataGridBuilder batch read โ stage all writes in a list โ 1 setDataCellValues() call. Total: 2 round-trips regardless of entity count. See Groovy Chapter II โ Performance Levers.throw new RuleStoppedException("msg") โ this class does not exist in the EPM Groovy API. It throws a MissingClassException, which surfaces as an internal error rather than a user-visible message.throwVetoException("Your message") โ no import, no new, just the helper method. This is the only supported mechanism for user-visible form validation messages in EPM Groovy.MissingMethodException on the first cube or dimension access call.application.getEssbaseCube("Plan1") โ this method doesn't exist. Or calling cube.getDimension("Account") โ the Cube object doesn't expose dimensions directly. These are the #1 and #2 Groovy API mistakes.operation.application.getCube("Plan1") for the cube. Use: cube.getOutline().getDimension("Account") for dimensions. Use: dim.findMember("Total_Revenue") for members (not getMember()).println() debug statements but the output can't be found.getRestApiClient() works in a form rule but fails in a standalone business rule.getRestApiClient() is only available in form rules (beforeSave, afterSave, onLoad) โ it is scoped to the form context and uses the current user's session. It does not exist in standalone business rules (EPM 25.11 limitation).HttpURLConnection with a service account credential. Create a Named Connection in EPM Workspace pointing to your EPM URL and use it in the rule for authentication. This is the documented pattern for business rule REST calls.getSubstitutionVariable("CurPeriod") returns null or throws MethodMissingException.getSubstitutionVariableValue() with the full Value suffix. The short form getSubstitutionVariable() doesn't exist โ returns null silently or throws MissingMethod depending on context.application.getSubstitutionVariableValue("CurPeriod") or cube.getSubstitutionVariableValue("CurPeriod"). To set: application.setSubstitutionVariableValue("CurPeriod", "Apr"). Note: getSubstitutionVariables() (plural, no "Value") returns a collection of all variables โ separate method.UPLOAD_FILE jobType) โ there is no direct uploadFileToServer() method on the connection object.SET CREATENONMISSINGBLK ON created millions of empty blocks. 2. FIX statement is missing the Year dimension โ calculates across all years instead of one. 3. Dynamic Calc account referenced inside a FIX โ re-evaluated per block instead of on-demand.SET CREATENONMISSINGBLK rule, then clear and reload data. 2. Add Year to every FIX statement. 3. Materialize the Dynamic Calc account if it's referenced frequently in stored calcs.SET CREATENONMISSINGBLK ON โ search all rule scripts. Every occurrence must be paired with SET CREATENONMISSINGBLK OFF immediately after the ENDFIX.@CREATEBLOCK approach instead of DATACOPY for zero-based seeding.SET CREATENONMISSINGBLK ON without a tight FIX scope, creating blocks at every sparse intersection regardless of whether data exists. Or: a poorly scoped @CREATEBLOCK ran across all years ร all scenarios.SET CREATENONMISSINGBLK OFF after ENDFIX. 3. Clear all data and reload from backup โ the only way to remove phantom blocks is to clear the data. 4. Verify normal block count after reload matches pre-explosion baseline.SET CREATENONMISSINGBLK on a 5-dimension FIX grew Vision Corp's cube from 48,000 to 1.2M blocks overnight. The application became unresponsive. Recovery required a full data reload from the previous night's backup โ 4-hour outage.&CurPeriod substitution variable is set to a future month, so YTD accumulates beyond the current period.importData from the pre-calc snapshot).assignRole command after addUser. Role assignment is a separate step from user creation.FX_Rate_Var = (CurrPeriodRate - PriorRate) ร LocalVolume; FX_Volume_Var = (CurrVolume - PriorVolume) ร PriorRate. Store prior period rates in a separate Version or use @PRIOR in a member formula. This eliminates the manual Excel FX bridge that every global Finance team currently runs every month.getCell() / setCell() per cost centre in a loop. 200 entities ร 2 calls = 400 network round-trips. At ~150ms each = 60 seconds minimum, plus overhead from EPM job orchestration per call.DataGridBuilder call. 2. Stage all writes in a list, commit with one setDataCellValues(). 3. Verify FIX scope on upstream calc scripts โ are they touching all years? 4. Check if the allocation basis account is Dynamic Calc (re-evaluated every getCell). 5. Check block count โ has a prior run created phantom blocks?ABOUT THIS GUIDE
All issues verified against Oracle EPM Cloud EPM 25.11. Block storage behaviour, API method names, and release-specific features are cross-referenced against Oracle's official Groovy Scripting Reference and EPM Cloud What's New documentation. When in doubt: check the Job Console first, Application Statistics second, Oracle Support third.