deepseek-ai/deepseek-harness · error
sessions.provide: missing prop "${name}"
Error message
sessions.provide: missing prop "${name}" What it means
Thrown while materializing a session's standard-props bundle (SessionProvideChannel.materializeInfo): a provider descriptor declared a prop name in its props roster, but its resolve(binding) contribution did not include that key. The provide channel is fail-loud by design — the declared roster and the resolver's actual output must agree exactly for every session binding — so a missing member aborts materialization instead of silently yielding an undefined prop. Materialization runs from the constructor, from record when a session binding arrives, and from provideInfo when a roster change rebuilds live bundles (a failing provide() registration rolls back and rethrows).
Source
Thrown at packages/client/runtime/src/client/sessions/provide.ts:152
const contributedProps = contribution.props ?? {}
for (const name of Object.keys(contributedHooks)) {
if (!(descriptor.hooks ?? []).includes(name)) {
throw new Error(`sessions.provide: undeclared hook "${name}"`)
}
}
for (const name of Object.keys(contributedProps)) {
if (!(descriptor.props ?? []).includes(name)) {
throw new Error(`sessions.provide: undeclared prop "${name}"`)
}
}
for (const name of descriptor.hooks ?? []) {
const source = contributedHooks[name]
if (source === undefined) throw new Error(`sessions.provide: missing hook "${name}"`)
if (Object.hasOwn(hooks, name)) throw new Error(`sessions.provide: duplicate hook "${name}"`)
hooks[name] = source
}
for (const name of descriptor.props ?? []) {
if (!Object.hasOwn(contributedProps, name)) throw new Error(`sessions.provide: missing prop "${name}"`)
if (Object.hasOwn(props, name)) throw new Error(`sessions.provide: duplicate prop "${name}"`)
props[name] = contributedProps[name]
}
}
return {
sessionId: binding.sessionId,
hooks,
props,
// The useProjection seat: key-addressed bare value faces off the
// session's projection store (open key space — never a static roster member).
projections: { faceOf: key => binding.session.projections.faceOf(key) },
}
}
/** Rebuild the static projection and the owner's live bundles, then republish the current one. */
private applyRosterChange(): void {
this.maybeInfoCache = this.materializeMaybeInfo()
this.host.rebuildBundles()View on GitHub (pinned to b150a551b8)
Solutions
- Make resolve(binding) return every name listed in descriptor.props for every binding — roster members must be unconditional values
- If the value genuinely may be absent, remove the name from descriptor.props (without a session every key already reads absent) or model it through the open projections key space instead of a roster member
- Check for typos and casing drift between the descriptor array and the resolver's returned object keys
- Add a unit test that runs the resolver against a representative binding and asserts Object.keys(contribution.props) matches descriptor.props
Example fix
// before — declared prop never contributed
sessions.provide({
props: ['workspaceTitle'],
resolve: (binding) => ({ hooks: { session: binding.session } }),
})
// after — every declared member is returned on every binding
sessions.provide({
props: ['workspaceTitle'],
resolve: (binding) => ({
props: { workspaceTitle: titleOf(binding) },
}),
}) Defensive patterns
Strategy: validation
Validate before calling
// registration-time smoke check: the resolver must cover the declared roster
function assertResolverCoversDescriptor(
descriptor: SessionProvideDescriptor,
sample: SessionBinding,
): void {
const contribution = descriptor.resolve(sample)
for (const name of descriptor.props ?? []) {
if (!Object.hasOwn(contribution.props ?? {}, name)) {
throw new Error(`provider would fail materialization: missing prop ${name}`)
}
}
} Try / catch
try {
const disposer = sessions.provide(descriptor)
} catch (error) {
// the channel already rolled the registration back; fix the descriptor/resolver mismatch, not the call site
} Prevention
- Keep the descriptor roster and the resolver in one adjacent block and review them together
- Never conditionally omit a declared member — declare only unconditional values
- Cover each provider with a test that materializes against a representative binding
- Treat any sessions.provide: message as a plugin-authoring bug, not a runtime condition to absorb
When it happens
Trigger: Registering sessions.provide({ props: ['x'], resolve }) whose resolve returns an object whose props omits 'x' — e.g. a conditional spread ...(cond ? { props: { x } } : {}), or a rename applied to the resolver but not the descriptor. The throw fires at the next materialization of any session bundle under that roster.
Common situations: Writing a new sessions.provide plugin and letting the declaration drift from the resolver; renaming a prop in one place during a refactor; returning hooks but forgetting props; fixture-driven test doubles that construct contributions by hand.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24).
Data as JSON: /api/errors/91de9212365b7128.
Report an issue: GitHub.