fix: raise SamplesException from latent computation when samples is None (resumed fits) - #1418
Merged
Merged
Conversation
… samples Running a latent script twice in the same output tree crashed the second (resumed) run with an opaque `AttributeError: 'NoneType' object has no attribute 'model'` from inside `latent_samples_from`. Root cause: when a fit is already complete the search short-circuits to `NonLinearSearch.result_via_completed_fit`, which reloads the samples off disk via `paths.samples`. If `samples.csv` was never written -- because `output.yaml`'s `samples: false` (as in `autolens_workspace_test`) or `general.yaml`'s `samples_to_csv: false` disabled it -- that raises `FileNotFoundError`, the caller swallows it and sets `samples = None`. `result.samples` is then None, and `latent_samples_from` dereferenced `samples.model` on it. Nothing is test-mode specific: with samples output enabled (the packaged default) the resume path reloads them and latent computation works unchanged. Behaviour chosen: raise `exc.SamplesException` rather than returning None. The existing `None` returns in this engine mean "the computation ran and nothing survived" (no latents declared, all values non-finite); a `None` samples argument is a missing input, a different class of failure that should not be silently conflated with a degenerate-but-valid result. `exc.SamplesException` is the established convention for invalid `Samples` input (see `Samples.__add__`). The message names the cause (resumed fit, samples output disabled) and the remedies. The guard is in the engine so the free-function entry point is covered too, and fires before latent configuration is consulted since a None samples object is unusable either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PzN9PZfG5zXMVku8ckSqkC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
On a completed-fit resume,
result_via_completed_fitreloads samples from disk and swallowsFileNotFoundErrorintosamples = None. When the output config disables samples persistence (output.yaml: samples: false, as in autolens_workspace_test),samples.csvnever exists, so every resumed result carriessamples=None— andcompute_latent_samples(result.samples)crashed withAttributeError: 'NoneType' object has no attribute 'model'insidelatent_samples_from(surfaced by the 2026-07-25 full health sweep).The reload path itself is fine: with
samples: true(the packaged default) resumed latent computation works unchanged — verified live.Change
Raise
exc.SamplesExceptionfrom the latent engine whensamples is None, with a message naming the resumed-fit cause and the remedies. Chosen over returningNone: the engine's existingNonereturns all mean "the computation ran and nothing survived" (documented), whereas a missing input is a categorically different failure that should not be conflated or silently hidden.SamplesExceptionis the established convention for invalidSamplesinput (Samples.__add__). Guard sits in the engine so the free-function entry point is covered too, and fires before latent config is consulted.autofit/non_linear/analysis/latent.py— guard +Raisesdocstringautofit/non_linear/analysis/analysis.py—compute_latent_samplesdocstring notetest_autofit/analysis/test_latent.py— two new tests (wrapper + engine, guard-ordering lock-in)Verification
pytest test_autofit/→ 1528 passed, 3 skipped (baseline 1526 + 2 new). Workspace_test latent smoke: fresh run passes end-to-end; resumed run now fails with the intentional, documentedSamplesExceptioninstead of an internalAttributeError(draft's acceptance criterion).Follow-up (not in this PR)
Making resumed latent-script runs actually pass needs the workspace's own call: autolens_workspace_test sets
samples: false; flipping it (globally or scoped to the latent scripts) verified to make both runs pass. Left as a deliberate workspace policy decision.Generated by Claude Code