livekit/livekit · warning

DependencyDescriptorReader: invalid spatial layer, should be

Error message

DependencyDescriptorReader: invalid spatial layer, should be less than the number of resolutions

What it means

ErrDDReaderInvalidSpatialLayer is returned by readFrameDependencyDefinition when the frame's spatial_id is greater than or equal to the number of resolutions declared in the structure. The spatial layer referenced by the frame does not exist in the declared resolution set, so frame dependencies cannot be interpreted. The bit reader buffer is invalidated before returning.

Source

Thrown at pkg/sfu/rtpextension/dependencydescriptor/dependencydescriptorreader.go:28

// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dependencydescriptor

import (
	"errors"
)

var (
	ErrDDReaderNoStructure              = errors.New("DependencyDescriptorReader: Structure is nil")
	ErrDDReaderTemplateWithoutStructure = errors.New("DependencyDescriptorReader: has templateDependencyStructurePresentFlag but AttachedStructure is nil")
	ErrDDReaderTooManyTemplates         = errors.New("DependencyDescriptorReader: too many templates")
	ErrDDReaderTooManyTemporalLayers    = errors.New("DependencyDescriptorReader: too many temporal layers")
	ErrDDReaderTooManySpatialLayers     = errors.New("DependencyDescriptorReader: too many spatial layers")
	ErrDDReaderInvalidTemplateIndex     = errors.New("DependencyDescriptorReader: invalid template index")
	ErrDDReaderInvalidSpatialLayer      = errors.New("DependencyDescriptorReader: invalid spatial layer, should be less than the number of resolutions")
	ErrDDReaderNumDTIMismatch           = errors.New("DependencyDescriptorReader: decode target indications length mismatch with structure num decode targets")
	ErrDDReaderNumChainDiffsMismatch    = errors.New("DependencyDescriptorReader: chain diffs length mismatch with structure num chains")
)

type DependencyDescriptorReader struct {
	// Output.
	descriptor *DependencyDescriptor

	// Values that are needed while reading the descriptor, but can be discarded
	// when reading is complete.
	buffer                         *BitStreamReader
	frameDependencyTemplateId      int
	activeDecodeTargetsPresentFlag bool
	customDtisFlag                 bool
	customFdiffsFlag               bool
	customChainsFlag               bool
	structure                      *FrameDependencyStructure
}

View on GitHub (pinned to ee45c3f0b1)

Solutions

  1. Ensure the keyframe carrying the updated structure (with all resolutions) is transmitted and not dropped
  2. Treat as transient during layer changes; skip the packet and wait for the next structure refresh
  3. Verify encoder configuration keeps spatial layer count consistent with the declared structure
  4. Log at debug/info only, as livekit's parser does, to avoid noise for expected transient cases

Example fix

// before
res, err := parseDD(payload)
if err != nil { return err }
// after
res, err := parseDD(payload)
if err == dd.ErrDDReaderInvalidSpatialLayer {
    // stale structure; wait for next keyframe/structure
    return nil
} else if err != nil { return err }
Defensive patterns

Strategy: validation

Validate before calling

func validSpatialLayer(dd *dd.DependencyDescriptor, structure *dd.Structure) bool {
    return structure != nil && int(dd.FrameDependencies.SpatialId) < len(structure.Resolutions)
}

Type guard

func isDDInvalidSpatialLayer(err error) bool {
    return errors.Is(err, dd.ErrDDReaderInvalidSpatialLayer)
}

Try / catch

if _, err := reader.Parse(buf); err != nil {
    if errors.Is(err, dd.ErrDDReaderInvalidSpatialLayer) {
        return nil // stale structure; skip until re-sync
    }
    return err
}

Prevention

When it happens

Trigger: Parsing a dependency descriptor whose FrameDependencies.SpatialId >= len(structure.Resolutions); happens when the publisher adds a spatial layer (e.g. enables simulcast/SVC) without updating the attached structure, or a structure-bearing packet was lost.

Common situations: SVC/simulcast streams where the encoder changes layer count mid-stream; clients that cached an old structure with fewer resolutions; interoperability with encoders that emit out-of-range spatial IDs.

Related errors


AI-assisted analysis of livekit/livekit@ee45c3f0b1 (2026-09-02). Data as JSON: /api/errors/0dbf3d04e0055ffe. Report an issue: GitHub.