
The notebook runs clean. Every cell green. Output looks right. You download the .py file, upload to Gradescope, and the autograder fails 9 out of 10 tests. The code did not change. The environment did.
Jupyter and standalone Python are not the same execution environment, and the differences hide in places no Python tutorial covers. The kernel keeps state your code does not show. Cell order does not match file order. Matplotlib plots disappear. File paths break. IPython magic commands silently vanish during export. Each one of these costs points on assignments where the code is technically correct.
This guide walks through the 6 specific reasons Jupyter notebooks fail on autograders, with the fix for each one, and a 6-step conversion process that catches every issue before submission.
The 6 Reasons Jupyter Code Fails on Autograders
Jupyter notebooks fail on autograders for 6 specific reasons: kernel state that scripts do not replicate, cell execution order that does not match file order, auto-printing of last expressions that scripts do not perform, matplotlib plots that vanish without %matplotlib inline, file paths that break when the working directory changes, and IPython magic commands that are not valid Python outside the notebook.
Each one fails silently. The notebook runs. The conversion runs. The autograder runs. Only the test output reveals the gap. The next 6 sections cover each cause and the fix.
Reason 1: The Kernel Remembers What a Script Forgets
A Jupyter kernel keeps every variable, function, and import in memory from the moment you start it until you restart it. A Python script starts fresh every time, with no memory of anything that happened before. Code that depends on kernel state runs perfectly in the notebook and fails immediately in a script.
Variables from previous cells that are not in your code
The most common failure pattern: a variable defined in cell 4 gets used in cell 12, but cell 4 was deleted before export. The kernel still has the variable in memory because the kernel never forgets. The .py file does not, because the .py file is just text. The autograder reports NameError: name ‘data’ is not defined on a variable the student is certain they defined.
Fix: after any cell deletion, restart the kernel and run all cells before exporting. A notebook that fails on restart produces a .py file that fails on the autograder.
Imports that look like they happened in your file
Cell 1 imports pandas as pd. Cell 7 deletes that import line during cleanup. Every other cell still uses pd.DataFrame(…) because the kernel still has pandas loaded. The .py export skips the import entirely, and the autograder throws NameError: name ‘pd’ is not defined on line 1 of the actual logic.
The same pattern hits imports of numpy, matplotlib, os, re, and every standard library module. Check the top of the .py file before submitting. The top of the .py file holds an explicit import statement for every library used anywhere in the code.
Functions defined in cells you forgot to keep
Helper functions defined in a scratch cell get used by later cells. The student cleans up the notebook before submission, deletes the scratch cell, and the kernel keeps the function in memory because the kernel was not restarted. The .py file has no function definition. The autograder fails with NameError: name ‘helper_function’ is not defined.
The fix is the same: restart kernel, run all, verify every cell still works, then export.
Reason 2: Cell Order Does Not Match File Order
Jupyter executes cells in the order you click “Run,” not in the order they appear in the notebook. The cell number on the left (the [1], [2], [3] markers) shows execution order, not position. When the .py file exports, cells get written in position order, which is often the wrong order for the code to work.
How Jupyter cell numbers actually work
Cell numbers track execution sequence across the whole session. A notebook with cells numbered [14], [3], [8], [1] is normal. The student wrote a function in cell 1, scrolled down to test it in cell 3, scrolled back up to fix something in cell 14, and so on.
The .py export ignores execution order. It writes cells from top to bottom. A function in cell 1 (positioned at the top) that depends on a variable defined in cell 14 (positioned in the middle) breaks in the .py file because the variable does not exist yet when the function runs.
The out-of-order execution trap
The trap fires when a student tests code in a different order than they wrote it. Example: cell 2 defines df = pd.read_csv(‘data.csv’). Cell 5 modifies df with df = df.dropna(). Cell 3 (between them in position, but executed last) uses df.head(). In the notebook, df is the modified version. In the .py file, cell 3 runs before cell 5, so df is the un-modified version. The autograder gets different output.
Fix: before exporting, click Kernel -> Restart Kernel and Run All Cells. A notebook that fails or produces different output on this step has order bugs that the .py file inherits. Fix the order in the notebook before exporting.
Why Restart Kernel and Run All is non-negotiable before submission
Restart Kernel and Run All simulates exactly what a Python script does: fresh interpreter, sequential execution from top to bottom. A notebook that passes this test produces a .py file that behaves identically. A notebook that fails produces a .py file that fails the same way.
This single step prevents the majority of “works in Jupyter, fails on autograder” bugs. Every notebook gets restarted and re-run from scratch before export. No exceptions.
Reason 3: Jupyter Auto-Prints, Scripts Stay Silent
The last expression in a Jupyter cell prints automatically. The same code in a .py file produces no output unless an explicit print() statement wraps it. Autograders read stdout, and a script that produces no output gets a 0 even when the calculation is correct.
The last-expression display problem
Example: a notebook cell with the single line df.head() shows the DataFrame’s first 5 rows. The same line in a .py file runs df.head(), returns the result, and discards it. Nothing prints. The autograder sees an empty stdout and marks the test failed.
Fix: wrap each expression in print() to make output visible to the autograder. df.head() becomes print(df.head()). 2 + 2 becomes print(2 + 2). Function return values get wrapped: result = my_function(input) followed by print(result).
print() vs display() vs return values
Three output mechanisms exist in Python, and only one works in autograders:
print(x) sends to stdout. Autograders read stdout. This is what works.
display(x) is an IPython function that produces rich output (HTML tables, plots) in the notebook. Outside Jupyter, display is not defined unless you import IPython.display, and even then the rich output gets lost.
return x from a function gives the value back to the caller. When nothing prints the returned value, the autograder sees nothing.
A test that expects “42” passes when the script runs print(42) and fails when the script runs display(42) or just 42 on a line by itself.
How Gradescope autograders read stdout
Gradescope captures the standard output stream of the Python process running the student’s code. Whatever appears via print() gets compared against the expected output. Anything that prints via display(), gets returned without printing, or relies on Jupyter’s auto-print feature is invisible to the autograder.
Check the expected output format in the assignment specification before writing any code. An expected output of “42\n” passes when a script prints “42” with a newline. The same test fails when a script returns 42 from a function without printing. Format matters as much as value.
Reason 4: Matplotlib Plots Vanish Outside the Notebook
Matplotlib plots display inline in Jupyter because of a hidden magic command and a notebook-specific rendering hook. Neither works in a .py file. Plots that appear perfectly in the notebook produce no output, no file, and no autograder credit when the script runs.
Why %matplotlib inline does nothing in a .py file
%matplotlib inline is an IPython magic command that tells the notebook to render plots inside cells. Outside the notebook, this line is not valid Python and causes a SyntaxError on import. When nbconvert exports the notebook, the magic command gets converted to a comment or stripped silently, depending on the version. Either way, the plot rendering mechanism it enabled stops working.
The .py file then calls matplotlib functions that build plots in memory and never display them. The autograder sees no output, no saved file, and no plot.
plt.show() vs plt.savefig() for autograder submission
plt.show() opens a window that displays the plot. In a script running on a headless autograder server with no display, plt.show() either does nothing or crashes with a backend error. Autograders running on Gradescope servers have no GUI and do not display plots.
plt.savefig(‘output.png’) writes the plot to a file. Autograders that check for plot output check for a saved file, not a displayed window. Replace every plt.show() with plt.savefig(‘expected_filename.png’) matching the filename the assignment specifies.
When the assignment expects a saved PNG file
Many CS courses test plot assignments by comparing the saved PNG against a reference image, pixel by pixel or via image similarity scoring. When the file does not exist at the expected path with the expected name, the test fails before any visual comparison runs.
Read the assignment specification for the exact filename. Some courses use output.png, some use plot.png, some use figure_1.png. The autograder looks for the exact string. A file called plot.png does not pass a test looking for output.png.
Reason 5: File Paths Break When the Working Directory Changes
A Jupyter notebook runs with its working directory set to the folder containing the notebook file. A Python script runs with its working directory set to wherever the autograder invokes it from, which is usually a different folder. Relative paths that load files in the notebook fail in the script because the relative path resolves to a different absolute location.
Why os.getcwd() returns something different in script mode
A notebook at /home/student/cs161/hw3/assignment.ipynb runs with os.getcwd() returning /home/student/cs161/hw3. A relative path like pd.read_csv(‘data.csv’) resolves to /home/student/cs161/hw3/data.csv and loads correctly.
The same code in a .py file invoked by an autograder runs with os.getcwd() set to wherever the autograder starts. Gradescope typically invokes scripts from /autograder/submission/ or similar. The relative path data.csv then resolves to /autograder/submission/data.csv, which does not exist, and pd.read_csv raises FileNotFoundError.
The right way to build a path with pathlib
Build paths from the script’s own location, not from the working directory. The pattern:
from pathlib import Path
script_dir = Path(__file__).parentdata_path = script_dir / 'data.csv'df = pd.read_csv(data_path)
Path(__file__).parent returns the folder containing the script, regardless of where the script gets invoked from. Joining with the data filename produces a path that always points to the right location.
For notebooks, __file__ is not defined. Use Path.cwd() as a fallback in the notebook version, and switch to Path(__file__).parent in the export.
What to check when FileNotFoundError appears on a file that loaded fine
A notebook that loads a file fine and an autograder that reports FileNotFoundError on the same line point to 3 specific issues:
The relative path in the code. ‘data.csv’ resolves against the working directory. ‘../data.csv’ reaches one folder up. Both depend on where the script gets invoked.
The expected location of the data file on the autograder. Most autograders place data files in the same folder as the submission. Some place them one folder up. The assignment specification or starter code shows the expected layout.
The case sensitivity of the filename. Jupyter on macOS is case-insensitive by default. Gradescope’s Linux autograder is case-sensitive. A file called Data.csv referenced as data.csv works locally and fails on the autograder.
Reason 6: IPython Magic Commands and Shell Commands Are Not Python
IPython magic commands (%magic, %%cellmagic) and shell escapes (!shell command) are notebook-only syntax. They are not valid Python. When nbconvert exports the notebook to .py, these lines get commented out, deleted, or wrapped in IPython calls that do not work outside a notebook.
%time, %matplotlib, %%capture, and other magics that break .py files
The most common magic commands that cause autograder failures:
%matplotlib inline: discussed in Reason 4
%time my_function(): times the execution of a single line, breaks completely outside the notebook
%%timeit: times an entire cell, only valid as the first line of a cell, fails as plain Python
%%capture: captures output of a cell, only works in IPython
%run script.py: runs another script from inside the notebook, fails as plain Python
%reset: clears all variables, only works in IPython
After nbconvert export, search the .py file for any line starting with %. Every line that survives the export breaks the script. Delete them or replace with proper Python equivalents.
Why ! shell commands fail outside Jupyter
!pip install pandas works in Jupyter because IPython interprets the exclamation mark as a shell escape. In a plain Python script, !pip install pandas is a SyntaxError. The same applies to !ls, !cat, !mkdir, and every other shell command students embed in notebooks.
A notebook that installs packages with !pip install exports that line as a comment or a SyntaxError. The autograder either runs without the package installed and throws ImportError, or the script fails to even start parsing.
The fix: install packages in your local Python environment before exporting. Remove all !command lines from the .py file. Autograders manage their own environments.
The nbconvert silent stripping problem
nbconvert sometimes strips magic commands silently and sometimes leaves them as comments. The behavior depends on the nbconvert version and the export template used. A %matplotlib inline line becomes # %matplotlib inline (a comment, harmless) on some versions, or get_ipython().run_line_magic(‘matplotlib’, ‘inline’) (a function call to IPython that fails outside the notebook) on others.
Open the .py file after export and search for get_ipython. Every match indicates a magic command that nbconvert tried to translate. Delete these lines. They serve no purpose in a standalone script.
How to Convert a Jupyter Notebook to an Autograder-Safe Python Script
The 6-step conversion process catches every issue from the previous 6 reasons. Follow it exactly. Skipping any step reintroduces the failure modes the previous sections covered.
Step 1: Restart kernel and run all cells
Open the notebook, click Kernel -> Restart Kernel and Run All Cells, and watch every cell run from top to bottom in fresh state. A cell that fails here means the notebook itself has bugs that depend on kernel state. Fix those first. A notebook that does not pass a restart-and-run-all produces a .py file that fails the same way.
Step 2: Export to .py using nbconvert
From the notebook’s File menu, choose Download as -> Python (.py). Or from the command line: jupyter nbconvert –to script your_notebook.ipynb. Both produce a .py file with the same content. Command line is faster for batch conversion.
Step 3: Open the .py in a plain editor and audit for magics
Open the .py file in VS Code, PyCharm, or any text editor. Search for these strings: get_ipython, %, !, display(. Every match indicates Jupyter-specific code that does not work outside the notebook. Delete or rewrite each one.
Step 4: Run the .py in a fresh terminal
Open a new terminal window. cd into the folder containing the .py file. Run python your_script.py. Read the output. Any error here is an error the autograder also hits. Fix every error before submitting.
Step 5: Test against the visible test cases the assignment provides
Most assignments provide sample inputs and expected outputs. Run the script against each sample input and compare. The fix list from previous reasons covers most discrepancies. For deeper testing patterns, Why Testing Matters in Programming Assignments (And How It Improves Your Grades) covers the testing discipline that prevents autograder surprises.
Step 6: Submit the .py, the .ipynb, or both based on the spec
Read the submission instructions exactly. Some courses accept .py only. Some accept .ipynb only. Some accept both and grade only the file the rubric specifies. Submitting the wrong format usually results in a 0 even when the code is perfect. For the full pre-submission technical checklist, Autograder-Safe Code: A Simple Checklist for Students covers the format-specific submission rules.
When the Autograder Tests a .ipynb File Directly
Some courses run autograders directly on the .ipynb file instead of an exported .py. This setup is common on Gradescope’s notebook autograder feature and in courses that use nbgrader. The 6 reasons still apply, but the failure modes shift.
What changes when the autograder runs your notebook
The autograder starts a fresh kernel, opens the notebook, and runs cells top to bottom in position order. It does not run cells in the order the student executed them. Cell order in the file matters more than ever because the autograder enforces top-to-bottom execution.
Restart-kernel-and-run-all expectations
A notebook that fails a manual Restart Kernel and Run All also fails the autograder, because the autograder does exactly the same thing. Verify the notebook passes this test locally before every submission. No exceptions.
Hidden cells and notebook metadata that trip submissions
Jupyter notebooks store metadata that students rarely see: cell tags, kernel specifications, and trusted-state flags. Some autograders check this metadata. A notebook saved with the wrong kernel name, or with cell tags from an unrelated assignment, fails on metadata checks before any code runs. Clear metadata using nbstripout or by manually editing the .ipynb file (which is JSON underneath) when an autograder rejects an otherwise-working notebook.
When to Get Expert Help Before the Deadline
Six environment mismatches between Jupyter and standalone Python cover most autograder failures, but real assignments often combine three or four of them in a single bug. A pandas import, a matplotlib plot, a relative file path, and an IPython magic command can all be wrong on the same submission. Catching them all under deadline pressure is hard.
MyCodingPal’s Python homework help pairs Python students with experts who convert .ipynb files into autograder-safe .py scripts, debug the specific reason a submission failed, and explain each fix so the same bug does not recur on the next assignment. The 6-hour urgent support window covers panic submissions. The standard delivery includes a written explanation tied to the rubric.
For students working in other languages, MyCodingPal’s coding homework help extends the same expert review process across Java, C++, R, JavaScript, and more.
If a bad grade already landed because of an environment issue, the conversion guide above does not help retroactively. How to Request a Regrade on a Programming Assignment Without Hurting Your Grade covers the email template, evidence, and timing for getting points back when an autograder failure was caused by an environment mismatch.
Frequently Asked Questions
Why does my Python code work in Jupyter but fail on Gradescope?
Jupyter and Gradescope run Python in different environments. The Jupyter kernel keeps state between cells, auto-prints last expressions, supports IPython magic commands, and runs cells in the order you executed them. Gradescope runs a fresh Python interpreter, prints nothing unless you call print(), rejects magic commands, and runs the file top to bottom. The 6 reasons above cover the specific mismatches.
How do I convert a Jupyter notebook to a Python script?
Use jupyter nbconvert –to script your_notebook.ipynb from the command line, or click File -> Download as -> Python (.py) in the notebook menu. After export, open the .py file and search for get_ipython, %, and !. Delete or replace every match before submitting. Run the .py in a fresh terminal to verify it works.
Why does my matplotlib plot not show up in the autograder?
Autograder servers run headless with no display, so plt.show() does nothing or crashes. Use plt.savefig(‘output.png’) with the exact filename the assignment specifies. The autograder reads the saved file, not a displayed window.
Why does pandas say ‘pd’ is not defined even though I imported it?
The import was made in a cell that got deleted or never re-run. Jupyter’s kernel kept pandas in memory because the kernel never restarted. The .py export only includes the import statements that were in the notebook at export time. Restart the kernel, run all cells, fix any errors, and re-export. Confirm the top of the .py file contains an explicit import pandas as pd statement.
What is the difference between .py and .ipynb submissions?
A .py file is plain Python source code that runs in any Python interpreter. A .ipynb file is a JSON document containing cells, outputs, and notebook metadata. Autograders running .py files use a fresh Python interpreter. Autograders running .ipynb files start a Jupyter kernel and execute cells top to bottom. The 6 reasons above apply to both formats, but the failure modes differ slightly. Check the assignment specification for the required format.