Problem
When unityctl asset refresh finds nothing new to compile but the project is already in a failed-compile state, the CLI prints only:
Error: Compilation errors exist from a previous build
and exits 1, without listing the errors. This happens via the hasCompilationErrors && !compilationTriggered branch in UnityCtl.Cli/AssetCommands.cs, which is driven by EditorUtility.scriptCompilationFailed on the Unity side — a bare bool, so no diagnostics travel with the response.
Compare with the compilationTriggered path, which prints the full file(line,col): error CSxxxx: ... list. From the caller's perspective the project is equally broken in both cases, but in one of them there is no way to find out why.
Why it matters
This is a dead end for agents (and humans) alike:
- Re-running
asset refresh returns the same one-liner — the refresh is a no-op since nothing changed, so compilation is never re-triggered and the errors are never re-emitted.
- The natural next step,
script eval to query the compile state, fails too: eval compilation itself can be affected, and there's no supported API agents reliably guess (InternalEditorUtility.GetAllScriptCompilationErrors etc. don't exist).
- The actual workarounds are non-obvious: touch a script to force a recompile, or scrape
logs --level error / Editor.log.
The state is easy to get into: errors introduced while the bridge wasn't connected yet, a failed compile from a previous session (failed compiles persist across editor restarts), or a compile kicked off by the editor itself rather than by a unityctl command.
Proposed fix
The plugin already receives full CompilerMessage lists via CompilationPipeline.assemblyCompilationFinished (UnityCtlBootstrap) and forwards them on the CompilationFinished event. Suggestion:
- Cache the last failed compile's messages plugin-side (a failed compile blocks the domain reload, so an in-memory cache survives;
SessionState could cover editor restarts).
- Include the cached messages in the
asset.refresh response when hasCompilationErrors && !compilationTriggered, and have the CLI print them through the existing DisplayCompilationErrors path.
- If no cached messages are available (errors predate plugin load), fall back to forcing a recompile via
CompilationPipeline.RequestScriptCompilation() so the messages get regenerated — or at minimum extend the error text with a recovery hint, e.g.:
Error: Compilation errors exist from a previous build
(run 'unityctl logs --level error' to see them, or modify a script and re-run 'unityctl asset refresh' to recompile)
Even just (3) would turn a dead end into a recoverable state; (1)+(2) make it seamless.
Problem
When
unityctl asset refreshfinds nothing new to compile but the project is already in a failed-compile state, the CLI prints only:and exits 1, without listing the errors. This happens via the
hasCompilationErrors && !compilationTriggeredbranch inUnityCtl.Cli/AssetCommands.cs, which is driven byEditorUtility.scriptCompilationFailedon the Unity side — a bare bool, so no diagnostics travel with the response.Compare with the
compilationTriggeredpath, which prints the fullfile(line,col): error CSxxxx: ...list. From the caller's perspective the project is equally broken in both cases, but in one of them there is no way to find out why.Why it matters
This is a dead end for agents (and humans) alike:
asset refreshreturns the same one-liner — the refresh is a no-op since nothing changed, so compilation is never re-triggered and the errors are never re-emitted.script evalto query the compile state, fails too: eval compilation itself can be affected, and there's no supported API agents reliably guess (InternalEditorUtility.GetAllScriptCompilationErrorsetc. don't exist).logs --level error/ Editor.log.The state is easy to get into: errors introduced while the bridge wasn't connected yet, a failed compile from a previous session (failed compiles persist across editor restarts), or a compile kicked off by the editor itself rather than by a unityctl command.
Proposed fix
The plugin already receives full
CompilerMessagelists viaCompilationPipeline.assemblyCompilationFinished(UnityCtlBootstrap) and forwards them on theCompilationFinishedevent. Suggestion:SessionStatecould cover editor restarts).asset.refreshresponse whenhasCompilationErrors && !compilationTriggered, and have the CLI print them through the existingDisplayCompilationErrorspath.CompilationPipeline.RequestScriptCompilation()so the messages get regenerated — or at minimum extend the error text with a recovery hint, e.g.:Even just (3) would turn a dead end into a recoverable state; (1)+(2) make it seamless.