1. Introduction

Compiler errors are brutal. They break your flow, demand precise knowledge of syntax and semantics, and often hide the real issue behind layers of jargon. For many developers, the cycle is the same: run the build, squint at the error dump, copy-paste the message into a search engine, and hope that someone else has fought the same battle before.

I’ve been experimenting with a tool that turns this process upside down. Instead of leaving you alone with raw error messages, it brings an LLM into the loop. The workflow is simple: feed the tool your code, let it attempt compilation, and if it fails, the model steps in. It explains the error in plain language, proposes a fix, and—if you approve—applies the change and tries compiling again. If the correction doesn’t fit, you can decline and stop the process.

What you end up with is more than just a compiler wrapper. It feels like having a junior collaborator who doesn’t just throw errors at you, but actively reasons about them, suggests improvements, and handles the tedious trial-and-error cycle for you. It’s not about replacing expertise, but about accelerating the feedback loop and making the compiler a little less of an adversary and a little more of a teammate.

2. The Problem with Compiler Errors

Compilers are designed for precision, not pedagogy. Their error messages often reflect the internal logic of parsing and type-checking rather than the way humans think about code. As a result, developers get output that’s technically correct but practically unreadable — pointing to syntax trees, tokens, or memory layouts instead of describing the actual mistake in plain terms.

When an error appears, the default reaction is often trial and error. Developers scan the output, try to guess what’s wrong, and if that fails, copy–paste the error message into a search engine or Stack Overflow. This disrupts the coding flow, breaks concentration, and can lead to hours spent bouncing between docs, forums, and code.

The fundamental issue is that compiler messages don’t bridge the gap between “what went wrong” and “what you should do next.” They diagnose symptoms but rarely prescribe solutions. Beginners find this intimidating, while even experienced developers waste time deciphering messages instead of fixing the underlying issue. This gap makes compilers feel like rigid gatekeepers rather than collaborators in the development process.

3. The Idea: Adding an LLM to the Loop

The frustration with compiler errors comes from their one-way nature: the compiler complains, you decipher. What if instead, the compiler could explain what it meant and suggest a fix? That’s where an LLM fits in.

The workflow is designed as a loop that layers AI reasoning on top of traditional compilation:

  • Input the code → the tool attempts to compile it as usual.

  • If compilation succeeds → output the result, job done.

  • If compilation fails → the compiler’s raw error message is passed to an LLM.

  • LLM explains the error → in plain, human-readable language, pointing out why it failed.

  • LLM proposes a fix → it generates a corrected version of the code or a minimal change to try.

  • User reviews the suggestion → if accepted, the tool recompiles with the fix; if rejected, the process ends.

  • Loop continues until compilation succeeds or the user decides to stop.

What makes this approach powerful is its interactive, iterative nature. The LLM isn’t replacing the compiler — it’s translating its opaque language into something actionable, then assisting with the trial-and-error cycle. The user stays in control, but the heavy lifting of interpreting and retrying is automated.

4. Designing the Prompt

At the heart of this tool is the prompt — the instructions that tell the LLM how to behave when it receives a compiler error. A poorly designed prompt can lead to verbose explanations, irrelevant guesses, or even “hallucinated” fixes that don’t compile. Getting the prompt right is what makes the system feel like a helpful assistant rather than a noisy chatbot.

What the prompt needs to achieve

  • Clarity: The LLM should explain the compiler error in plain language without drowning the user in jargon.

  • Actionability: It should propose a minimal, concrete fix rather than rewriting the entire program.

  • Structure: Responses should be predictable, ideally split into two parts: explanation and suggested fix.

  • Restraint: The model shouldn’t invent errors or over-correct; it should only address the failure at hand.

The prompt structure use looks like this:

You are an expert Python developer specialized in building AI-driven tools. You are using LangChain with Ollama and the model qwen2.5:14b.

Your task is to build a Python tool that:

 - Accepts Fortran source code as input.

 - Attempts to compile the code using gfortran.

Behavior:

 - If compilation succeeds:

   - Report success.

 - If compilation fails:

   - Capture the compilation error.

   - Generate a response with two parts:

     (a) A clear and concise explanation of the error.

     (b) A proposed corrected version of the Fortran code.

   - Ask the user whether to apply the suggested fix (yes/no).

     - If yes: Replace the code with the suggestion and attempt compilation again.

     - If no: Stop the process.
 - If recompilation afteryesstill fails:

   - Loop back to step 2 with the new error and propose a different fix.

Requirements:

 - All prompts and outputs must be in English.

 - Use loguru for logging to track every step of the process, including compilation attempts, errors, suggestions, user responses, and loop iterations.

5. Building the Tool

workflow Workflow diagram showing how the LLM-helper Compilysis operates

The architecture of the helper is intentionally lightweight. At its core, it acts as a wrapper around the compiler with an LLM bolted on for interpretation. The loop works like this:

  1. Compiler interface – the tool runs a standard compiler command, here gfortran and captures the output.

  2. Error handling – if the exit code indicates failure, the raw error message is collected.

  3. LLM interface – the error message, along with the surrounding code, is sent to the LLM with the structured prompt. A special prompt is sent here to the LLM in order to get a clear explanation on what is wrong with the code snippet :

You are an expert Fortran programmer. Analyze the following compilation error and provide a simple, concise explanation.{context}

FORTRAN CODE:
{code}

COMPILATION ERROR (Attempt #{attempt}):
{error}

Please provide a clear and concise explanation of what is causing this compilation error. Focus on:
1. What the error means in simple terms
2. Which part of the code is problematic
3. Why this error occurs
4. If this is a repeated attempt, explain why previous fixes might have failed

Keep your explanation brief and focused.
  1. Feedback cycle – the LLM’s explanation and fix are displayed thanks to the given prompt below. If the user accepts, the tool applies the patch and recompiles; if not, the loop ends.
You are an expert Fortran programmer. Fix the following Fortran code based on the compilation error.{context}

CURRENT FORTRAN CODE:
{code}

CURRENT COMPILATION ERROR:
{error}

Provide ONLY the corrected Fortran code in plain text format. 
DO NOT use markdown code blocks (```fortran or ``` tags).
DO NOT include explanations or comments.
Output should start directly with "program" or "subroutine" or whatever the first line of code should be.

Example of correct output format:
program example
    implicit none
    integer :: i
    i = 5
end program example

CORRECTED CODE:

This design doesn’t replace the compiler or change how it works. Instead, it layers AI on top, turning the static error output into a dialogue. The main considerations during development were:

  • Performance – avoid too much latency between compile attempts.

  • Cost – keep token usage efficient with compact prompts, this is the bottleneck of every tool you might build.

  • Accuracy – validate that the LLM’s fixes don’t introduce new errors.

The end result is a command-line utility that feels surprisingly natural: instead of staring at error logs, you’re having a back-and-forth conversation with your “compiler sidekick.”

6. Example in Action

Consider this simple case in Fortran:

program hello
print *, "Hello, world!"
end progrm hello

A human eye spots the missing a in end progrm right away, but here’s what happens with the tool:

2025-09-17 10:01:14 | INFO     | __main__:_setup_logger:48 - Logger initialized
2025-09-17 10:01:14 | INFO     | __main__:_initialize_llm:53 - Initializing Ollama with model: qwen2.5:14b
2025-09-17 10:01:14 | SUCCESS  | __main__:_initialize_llm:55 - Ollama LLM initialized successfully
2025-09-17 10:01:14 | INFO     | __main__:debug_fortran_code:331 - Starting Fortran code debugging process
2025-09-17 10:01:14 | INFO     | __main__:debug_fortran_code:342 - Compilation attempt #1
2025-09-17 10:01:14 | INFO     | __main__:compile_fortran:70 - Attempting to compile Fortran code
2025-09-17 10:01:15 | WARNING  | __main__:compile_fortran:91 - Compilation failed
2025-09-17 10:01:15 | INFO     | __main__:debug_fortran_code:358 - Code failed to compile on attempt #1, starting AI analysis

 Compilation failed (attempt #1). Analyzing error with AI...

2025-09-17 10:01:15 | INFO     | __main__:analyze_error:121 - Analyzing compilation error with AI (attempt #1)

Compiler output is sent to the LLM :

error: Unclassifiable statement at (1)
error: Expecting END PROGRAM statement

LLM explanation:

2025-09-17 10:02:27 | SUCCESS  | __main__:analyze_error:160 - Error analysis completed for attempt #1
🔍 ERROR ANALYSIS (Attempt #1):
==================================================
### Compilation Error Analysis

#### 1. What the error means in simple terms:
The compiler is expecting an `END PROGRAM` statement to properly close the program block but instead encounters the end of the file without finding it.

#### 2. Which part of the code is problematic:
The line `end progrm hello ! <- typo: should be "end program hello"` contains a syntax error due to a misspelling.

#### 3. Why this error occurs:
The Fortran keyword `END PROGRAM` must be spelled correctly for the compiler to recognize it as the end of the program block. The incorrect spelling `progrm` causes the compiler to not identify the intended termination point, leading to an unexpected EOF (End Of File) error.

#### 4. If this is a repeated attempt, explain why previous fixes might have failed:
If previous attempts at fixing the issue did not resolve it, it likely means that the typo was not corrected or there were other unnoticed errors in the code. Ensuring that `end program hello` is correctly spelled and placed is crucial for resolving this error.

### Summary
The compilation error occurs because of a misspelling (`progrm` instead of `program`) on line 3, which prevents the compiler from recognizing the end of the program block properly. Correcting this typo should resolve the issue.
==================================================

LLM proposed fix:

2025-09-17 10:02:27 | INFO     | __main__:suggest_fix:178 - Generating code fix suggestion (attempt #1)
2025-09-17 10:02:35 | SUCCESS  | __main__:suggest_fix:252 - Code fix suggestion generated for attempt #1

============================================================
PROPOSED CODE CHANGES:
============================================================

ORIGINAL CODE:
----------------------------------------
program hello
print *, "Hello, world!"
end progrm hello ! <- typo: should be "end program hello"

PROPOSED FIXED CODE:
----------------------------------------
program hello
print *, "Hello, world!"
end program hello

============================================================

You can then chose to keep this proposed fix or not, if so, the code will be recompiled and saved if it works, otherwise it goes back into the loop :

Do you want to apply this fix? (yes/no): yes
2025-09-17 10:04:02 | INFO     | __main__:get_user_confirmation:312 - User accepted the proposed fix

🔧 Applying fix #1 and attempting recompilation...
2025-09-17 10:04:02 | INFO     | __main__:debug_fortran_code:342 - Compilation attempt #2
2025-09-17 10:04:02 | INFO     | __main__:compile_fortran:70 - Attempting to compile Fortran code
2025-09-17 10:04:03 | SUCCESS  | __main__:compile_fortran:88 - Compilation successful!
2025-09-17 10:04:03 | SUCCESS  | __main__:debug_fortran_code:352 - Code compiled successfully after 2 attempts!Code compiled successfully after 2 attempts!Fixed code saved to: fortran_mistakes/01_syntax_error.fixed.f90
2025-09-17 10:04:03 | INFO     | __main__:main:431 - Fixed code saved to: fortran_mistakes/01_syntax_error.fixed.f90

For trivial bugs like this, the helper saves seconds. For trickier type mismatches, missing headers, or template errors, it can save minutes or even hours by translating obscure messages into direct fixes.

7. Strengths and Limitations

Strengths

  • Lowers the barrier for newcomers who struggle with cryptic error messages.

  • Speeds up debugging by translating “compiler-speak” into natural language.

  • Keeps developers focused in their workflow instead of bouncing between docs and forums.

  • Feels interactive, almost like pair-programming.

Limitations

  • The LLM may propose fixes that compile but don’t match the developer’s intent nor the coding standards of the code.

  • Some errors are too complex for a single pass; the loop can stall.

  • Still needs human oversight — you can’t blindly trust every suggestion.

Looking ahead, tighter integration with build systems could make the tool even smoother, with fixes suggested inline rather than through a wrapper script.

8. Reflections and Next Steps

Building this helper was less about replacing expertise and more about augmenting it. The LLM doesn’t make compiler theory disappear — it just makes the process of handling errors less punishing. Along the way, I realized how much of debugging is about translation: turning formal error codes into understandable, actionable steps.

Next steps could include:

  • Extending support for multiple compilers and languages. This can be useful when trying to port a code on different HPC machines

  • Adding richer interaction, where the user can ask why a fix works.

  • Experimenting with fine-tuned models specialized for compiler outputs. Thus improving the proposed solution and make them relie on your coding standards.

The journey suggests a bigger idea: compilers don’t have to be passive gatekeepers. With AI in the loop, they can become active teaching tools.

9. Conclusion

Compiler errors have always been treated as obstacles to overcome, but they don’t have to be. By pairing a traditional compiler with an LLM, we can reframe error handling as a dialogue rather than a guessing game.

The tool built is a small step in this direction — it listens to the compiler, explains what went wrong, proposes a fix, and lets the developer stay in control. It’s not perfect, but it shows what’s possible when we treat compilers less like blunt instruments and more like collaborators.

Looking ahead, there are several exciting directions this work could take: supporting multiple compilers for better portability, fine-tuning the model so it adheres to specific coding standards and conventions, or even experimenting with smaller, specialized models that outperform general-purpose ones on compiler tasks. Each of these paths brings us closer to a future where compilers don’t just enforce rules, but actively help developers write better code.

In the end, the compiler doesn’t just talk back — it helps you move forward.

The tool describe in this blog post can be found and used by clicking on the image below.

codelsd_logo

Like this post? Share on: TwitterFacebookEmail


Thibault Marzlin is an Research Engineer working on COOP Python tools.

Keep Reading


Published

Category

Pitch

Tags

Stay in Touch