What is the screen telling you?
The Design and Preview tabs both ask the gateway for report data. If the Script data source fails to compile, no data keys come back and the designer shows Data Collection Error. The warning under it names the actual fault:
WARN: Unable to compile script.SyntaxError: mismatched input '\n' expecting INDENT (<function:updateData>, line 1)
Read it one token at a time.
| Token | Meaning | Where to look |
|---|---|---|
Unable to compile script |
The parser rejected the script before it ran. No query executed and no database call was made. | Leave SQL, connections, and parameters alone for now. |
SyntaxError |
The text breaks Python grammar. The logic is not the problem yet. | Structure: colons, indentation, block openers. |
mismatched input '\n' expecting INDENT |
The parser read a block opener ending in : and then a newline. The next line was not indented deeper, so the block has no body. |
A def, if, for, or try with nothing indented under it. |
<function:updateData> |
The code being compiled is the body of the report's updateData function. |
The Script data source editor. |
line 1 |
The first line of the text you typed or pasted in the editor, counted from the body. | The first editable line. |
What the screen is telling you: the first line of your body opens a block and the second line fails to indent under it. A script that ran on v7.9 and fails at line 1 on v8.1.25 almost always has a structural paste problem. The SQL is not involved.
Check before moving on: confirm the message says SyntaxError or Unable to compile. A runtime error such as KeyError or a database exception means compilation already succeeded. In that case, skip to the bindings section.
Where in the stack does the compile fail?
The gateway log trace shows the call path:
-
ReportingGatewayHook$RPC.getReportData: the Designer's Preview request arrives at the gateway over RPC. -
ScriptReportDataSource.gatherData: the report module starts the Script data source. -
ScriptManager.compileFunction: the gateway wraps your body text in a function with the fixed signatureupdateData(data, sample). -
ParserFacade.parse→org.python.core.PySyntaxError: Jython rejects the wrapped text.
This path has two consequences:
- Compilation happens on the gateway, not in the Designer. Every Preview click recompiles.
- The function wrapper is generated from the signature. Your text becomes its body, and "line 1" means your first line.
The log copy of the error shows an empty function name ((, line 1)). The Designer shows <function:updateData>. Both point to the same place.
Check:
- Open the gateway logs and click Preview once.
- Find the new
PySyntaxErrorentry whose timestamp matches the click. - Confirm
ScriptReportDataSource.gatherDataappears in that trace.
If the trace shows a project library or another script instead, you are debugging the wrong script.
Is the def line in the body twice?
The v8.1 Script data source editor supplies the signature def updateData(data, sample): as a fixed header above the editable area. You write only the body.
A copy of the v7.9 script that includes its own def updateData(data, sample): line produces this wrapped text:
def updateData(data, sample): # supplied by the editor
def updateData(data, sample): # your line 1 (pasted)
dbConnection = "redacted" # your line 2, same level as line 1
...
Your pasted def opens a block at line 1. Line 2 sits at the same indentation, so the nested function has no body. The parser reports expecting INDENT at line 1, which matches the error exactly. The tag is right; the binding is wrong: the code is valid, but it sits inside a wrapper it was never written for.
- Open the report, go to the Data tab, and select the Script data source.
- Read the first editable line. If it reads
def updateData(data, sample):, delete it. - Remove one level of indentation from every remaining line, if the pasted body was indented under the pasted
def.
Check: the first editable line now reads dbConnection = "...". No def line exists anywhere in the body.
How far should the body be indented?
The body only needs to be consistent relative to the editor-supplied header. Get the correct starting column from the editor itself; do not guess it.
- Add a temporary new Script data source to the same report.
- Note the column where its default placeholder line starts: column zero, or one tab in.
- Start your top-level statements (
dbConnection, the three query strings,startDate, therunPrepQuerycalls, the finaldata["top"]assignments) in that same column. - Delete the temporary data source.
Each body layout gives a different outcome:
| Body layout | Compile result | Report result |
|---|---|---|
Pasted def + body at the same level |
SyntaxError ... expecting INDENT, line 1 |
Data Collection Error |
Pasted def + body indented under it |
Compiles cleanly | Silent failure. updateData only defines an inner function that nothing calls. top and top2 are never created, and tables bound to them render empty or show missing-key warnings. |
No def, body at the placeholder column, one whitespace type |
Compiles | Runs; continue to the bindings checks |
No def, mixed tabs and spaces |
IndentationError or SyntaxError at a later line number |
Data Collection Error |
The second row is the trap. It clears the error without fixing anything. The only correct configuration is the third row.
Check: every top-level statement starts in the placeholder column. Each nested statement is indented exactly one level deeper than its opener.
Are tabs and spaces mixed?
Jython 2.7 expands a tab to the next multiple of eight columns. A line indented with one tab and a neighboring line indented with four spaces sit at different block levels, even when the editor draws them aligned.
The original script uses tabs. The commented-out lines near the end of the first dock block begin with two tab characters before newRow. Copying the script through an external text editor can convert tabs to spaces in some lines and leave others untouched. The result looks fine on screen and fails in the parser.
This script is exposed because it nests four levels deep:
if ...: # level 1
for i in range(5): # level 2
try: # level 3
gw = ... # level 4
except:
gw = None
It repeats that pattern in six blocks, which gives many places for one inconsistent line to hide.
- Copy the full body out of the report editor into a text editor with visible whitespace (show all characters).
- Scan the leading whitespace of every line. Tabs and spaces render with different markers.
- Pick one convention: tabs only, or four spaces only.
- Convert the whole body with the editor's tab-to-space or space-to-tab function.
- Paste the result back into the Script data source.
Check: with whitespace visible, every indented line starts with a single character type. The depth goes up by one unit per nesting level: if/elif → for → try/except → assignment.
Does Preview get past the compile now?
Click Preview. Once the def duplication and whitespace are fixed, the line-1 error disappears. Any remaining error now carries a meaningful line number and type.
| What the screen shows | Layer | Next action |
|---|---|---|
SyntaxError at line N > 1 |
Parser | Go to body line N. Look for a missing colon, an unclosed triple-quoted string, or an elif not aligned with its if. |
IndentationError / unindent does not match |
Parser | Whitespace is still mixed at that line. Repeat the whitespace pass. |
KeyError: 'StartDate' |
Runtime | The report parameter name does not match. See the bindings section. |
| Database or SQL exception | Runtime | Connection name or SQL dialect. See the bindings section. |
| No error, tables empty | Runtime | An inner def is still wrapping the body, the date has no completed jobs, or table data keys do not match. |
Check: the gateway log gets no new PySyntaxError entry on Preview. The Data Collection Error banner no longer appears.
Are the parameter and database bindings right?
Once the script compiles, it fails or returns nothing only through its runtime inputs. Trace each one to where it is set.
| Setting | Location | Effect if wrong |
|---|---|---|
StartDate report parameter |
Report Data tab, Parameters |
data["StartDate"] raises KeyError. The key is case-sensitive. |
Parameter type of StartDate
|
Parameter default expression | The value feeds CAST(? AS DATE). A string in the wrong format fails the cast or matches the wrong day. |
dbConnection string |
Gateway database connections list | Must match a connection name exactly, or every runPrepQuery call fails. |
| SQL dialect | Database behind that connection |
TOP, DATEDIFF(mi, ...), DATEPART(HOUR, ...), and CAST(... AS DATE) are SQL Server syntax. Another database engine rejects them. |
Data keys top and top2
|
Design tab, table component data key | A table bound to any other key renders empty. |
Isolate the database from the report by running a single query in the Designer Script Console:
q = """
SELECT top 5 (DATEDIFF(mi,timein,filling_start_time)) AS 'WaitTime'
FROM truckdriver
WHERE job_status = 'Completed' AND Loadout_ID = ?
ORDER BY WaitTime DESC
"""
print system.db.runPrepQuery(q, [1], database="YourConnectionName")
Check: the console prints a dataset with rows. Then confirm on the Design tab that both tables list top and top2 as their data keys.
Are empty results and swallowed exceptions hiding data?
Two habits in the script make a working report look broken, or a broken one look working.
Truthiness tests on result sets. if gravewait1 or daywait1 or swingwait1: relies on an empty query result evaluating false. An explicit length test reads unambiguously and behaves the same:
if len(gravewait1) > 0 or len(daywait1) > 0 or len(swingwait1) > 0:
Bare except: clauses. The intended catch is IndexError, raised when a shift returned fewer than five rows. A bare except: also catches every other error, including a wrong column index or a type problem, and turns it into blank cells. Narrow each clause:
try:
gw = gravewait1[i][0]
gf = gravefill1[i][0]
gt = gravetotal1[i][0]
except IndexError:
gw = None
gf = None
gt = None
The dock selection logic works as follows. ds1 (key top) receives the first dock, in order 1, 2, 3, whose wait queries returned rows. ds2 (key top2) receives the second such dock. A third active dock is never displayed. If three docks run on the same date, that is a report design gap, not a script fault.
Check: preview a StartDate where dock 1 had no completed jobs. top should show "Dock 2 Stats" (or Dock 3), and top2 should show the next active dock or stay empty.
Should the repeated dock blocks be collapsed?
The script repeats the same 30-line block six times. Each copy is another place for an indentation mismatch or copy error, such as a wrong dock variable in one try. A loop-driven body produces the same two datasets from one copy of the logic.
Keep the three query strings (querywait, queryfill, querytotal) exactly as they are, above this block. Use one whitespace convention throughout and start at the placeholder column.
dbConnection = "YourConnectionName"
startDate = data["StartDate"]
cols = ['gwait', 'gfill', 'gtotal', 'dwait', 'dfill', 'dtotal', 'fwait', 'ffill', 'ftotal']
shifts = [(0, 7), (8, 15), (16, 23)] # grave, day, swing
kinds = [('wait', querywait), ('fill', queryfill), ('total', querytotal)]
res = {}
for dock in (1, 2, 3):
for kind, q in kinds:
res[(dock, kind)] = [system.db.runPrepQuery(q, [dock, startDate, lo, hi], database=dbConnection) for lo, hi in shifts]
active = [d for d in (1, 2, 3) if any(len(r) > 0 for r in res[(d, 'wait')])]
tables = []
for dock in active[:2]:
ds = system.dataset.toDataSet(cols, [])
ds = system.dataset.addRow(ds, ['', '', '', '', 'Dock %d Stats' % dock, '', '', '', ''])
ds = system.dataset.addRow(ds, ['', 'Grave', '', '', 'Day', '', '', 'Swing', ''])
ds = system.dataset.addRow(ds, ['Max Wait Time', 'Max Fill Time', 'Max Total Time'] * 3)
for i in range(5):
row = []
for s in range(3):
for kind, q in kinds:
pds = res[(dock, kind)][s]
row.append(pds[i][0] if i < len(pds) else None)
ds = system.dataset.addRow(ds, row)
tables.append(ds)
while len(tables) < 2:
tables.append(system.dataset.toDataSet(cols, []))
data["top"] = tables[0]
data["top2"] = tables[1]
How this differs from the original:
-
Same: 27 queries, the same parameters, the same dock-priority order for
topandtop2, the same header rows, and the same column order (grave wait/fill/total, then day, then swing). - Different: missing values are blanked per cell. The original blanks all three cells of a shift if any one of its queries is short. Wait, fill, and total queries share the same WHERE clause, so their row counts normally match. Confirm on real data.
- Not changed: round trips. The loop keeps 27 database calls per render. Reducing them requires a single windowed SQL query per dock, which is a separate change.
Check: preview the same StartDate with the original and refactored bodies (swap them in the editor). Compare every cell of top and top2.
How do you prove the fix end to end?
- Open the Script data source. Confirm the first editable line is a statement, not
def updateData(data, sample):. - Copy the body into a whitespace-visible editor. Confirm one indentation character type and one unit per nesting level, then paste it back.
- Click Preview. Confirm the gateway log has no new
PySyntaxErrorentry fromScriptReportDataSource.gatherDataat that timestamp. - In the Data tab key browser, confirm
topandtop2appear as data keys. They exist only if the body actually ran. - Set
StartDateto a date with known completed jobs. Confirm the first table shows the correct dock header, the Grave/Day/Swing row, the Max Wait/Fill/Total row, and up to five minute values per column. - Set
StartDateto a date where dock 1 was idle. Confirm the first table shows the next active dock and the second table shows the following dock or stays empty. - Spot-check one cell. Run the matching
querywait(or fill/total) query in the Script Console with the same dock, date, and hour range, and confirm the top value matches the report cell.
FAQ
Why does an Ignition report script say "mismatched input '\n' expecting INDENT" on line 1?
Line 1 of your body opens a block, usually a pasted def updateData(data, sample):, and line 2 is not indented under it. The Script data source already supplies that signature. Delete the pasted def line and align the body with the editor's default placeholder column.
Why does a report script that worked in Ignition 7.9 fail after rebuilding in 8.1?
A copied script that carries its own def updateData line, or whose tabs were partly converted to spaces in transit, will not compile inside the v8.1 function wrapper. Remove the duplicate header and normalize all leading whitespace to a single type. Then confirm on Preview that the gateway log shows no PySyntaxError.
Why does my report show empty tables with no error after fixing the indentation?
If the body is still indented under a pasted def, it compiles into an inner function that nothing calls, so data["top"] is never set. Remove that def, confirm top and top2 appear in the data key browser, and check that each table's data key matches.