Quick answer: Apply [DeallocateOnJobCompletion] to a NativeArray on at most one job in a dependency chain — usually the last. Anything reading the array after completion errors out. Prefer explicit .Dispose() for clarity.
Job runs, completes, you try to read the result, and Unity throws “The Allocator label is not valid.” The buffer was freed before you got to it — almost always because of a misplaced DeallocateOnJobCompletion attribute.
The Symptom
Errors like “The NativeArray has not been allocated, it has been disposed, or its safety handle has expired.” The job ran successfully; the read happens after.
What Causes This
The [DeallocateOnJobCompletion] attribute frees the NativeArray when the job is done. If you intend to read the buffer on the main thread after the job completes, you must not deallocate it during the job. The attribute should be on buffers the job uses internally and that no one else needs after.
The Fix Patterns
Pattern 1: Explicit Dispose. Most reliable.
var data = new NativeArray<float>(1024, Allocator.TempJob);
var job = new MyJob { data = data };
var handle = job.Schedule();
handle.Complete();
// Read on main thread before disposing
Debug.Log($"first: {data[0]}");
data.Dispose();
Pattern 2: DeallocateOnJobCompletion for fire-and-forget.
[BurstCompile]
public struct MyJob : IJob
{
[DeallocateOnJobCompletion]
public NativeArray<float> scratch; // freed by job, never read after
public NativeArray<float> result; // not freed; main thread reads after
public void Execute() { ... }
}
Use Deallocate only on buffers the main thread will not touch. Output buffers must be disposed manually after read.
Chained Jobs
If Job A schedules with a buffer, Job B depends on A and uses the same buffer, only B may DeallocateOnJobCompletion that buffer. Putting it on A frees the buffer before B runs.
var data = new NativeArray<float>(100, Allocator.TempJob);
var a = new JobA { data = data };
var handleA = a.Schedule();
var b = new JobB { data = data };
var handleB = b.Schedule(handleA); // depends on A
handleB.Complete();
data.Dispose(); // safe: B finished
Allocator Choice
- Allocator.Temp — one frame, main thread only. Fast.
- Allocator.TempJob — up to four frames, jobs allowed. Errors if not disposed within ~4 frames.
- Allocator.Persistent — live forever. Use for long-lived buffers; explicit Dispose required.
The error “Allocations of TempJob may not survive longer than 4 frames” comes from forgetting to Complete + Dispose within the budget.
Verifying
Run with Job Safety Checks on (default in Editor). The first frame after a misuse, you get the precise error and a stack trace pointing at the access. Disable safety checks only for measured benchmarks.
“Deallocate where the buffer dies. Dispose explicitly otherwise. Safety checks tell you the rest.”
Related Issues
For Burst managed allocation warnings, see Burst BC1006. For NativeArray leaks, see NativeArray leak.
Free where the buffer dies. Read before. Dispose explicit.