affaan-m/ECC · error · Error
sampleIntervalMs must be positive and finite
Error message
sampleIntervalMs must be positive and finite
What it means
createControlPlaneViewSource validates its sampleIntervalMs dependency: if provided it must be a finite number greater than zero. The interval controls how long cached control-plane snapshots live before a refresh; a zero, negative, or non-numeric value would break the caching/expiry logic, so it is rejected at construction time.
Solutions
- Pass a positive finite number, e.g. sampleIntervalMs: 5000
- Convert config values with Number()/parseInt and validate with Number.isFinite before passing
- If you want 'no sampling', omit sampleIntervalMs entirely and use the 5000ms default rather than passing 0
- Fix the upstream computation that produced NaN or 0
Example fix
// before
createControlPlaneViewSource({ sampleIntervalMs: process.env.SAMPLE_MS })
// after
const ms = Number(process.env.SAMPLE_MS);
createControlPlaneViewSource({ sampleIntervalMs: Number.isFinite(ms) && ms > 0 ? ms : undefined }); Defensive patterns
Strategy: validation
Validate before calling
function validInterval(ms) { return ms === undefined || (Number.isFinite(ms) && ms > 0); }
// only pass sampleIntervalMs if validInterval(sampleIntervalMs) Type guard
const isPositiveFinite = (n) => typeof n === 'number' && Number.isFinite(n) && n > 0;
Try / catch
try { createControlPlaneViewSource(deps); } catch (e) {
if (String(e.message).includes('sampleIntervalMs')) console.error('Fix sampleIntervalMs:', deps.sampleIntervalMs);
else throw e;
} Prevention
- Coerce env/config values with Number() before passing
- Never pass 0 to mean 'no delay'; omit the option instead
- Unit-test config parsing that feeds sampleIntervalMs
- Keep interval values as numbers end-to-end, not strings
When it happens
Trigger: Calling createControlPlaneViewSource({ sampleIntervalMs: 0 }) or { sampleIntervalMs: -1 }, or passing a non-finite value (NaN, Infinity) or a string like '5000' instead of a number. Omitting it is fine (defaults to 5000).
Common situations: Reading the interval from an env var or query string without Number conversion (getting a string); a config file exporting 0 meaning 'no delay'; arithmetic producing NaN before construction.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- application config exceeds local size limit
- Asset request_id/modality does not match the bundle
- at least one genre spec is required
- bundle collections must be lists
- candidate configuration exceeds 1 MiB
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/90a94961f4d9d17b.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/control-pane/control-plane-view.js:325
limits: [
'Advisories use static thresholds; no learned threshold and no conflict-reduction claim.',
'Projection is a display over the shipped channels x_tree, x_overlap, x_dep; it does not change risk.',
'Inventory rows are declared-only observations; leases are not locks.',
'The view does not steer, pause or lock any agent.'
]
};
}
/**
* Stateful view builder for a long-lived server: keeps one projection window
* so z-scores roll over ticks. `buildSnapshot()` is injected (it is the
* control-pane snapshot with includeProximity: true).
*/
function createControlPlaneViewSource(deps = {}) {
const window = deps.window || createProjectionWindow(deps.projection || {});
const clock = deps.clock || Date.now;
const interval = deps.sampleIntervalMs === undefined ? 5000 : deps.sampleIntervalMs;
if (!Number.isFinite(interval) || interval <= 0) throw new Error('sampleIntervalMs must be positive and finite');
let cached = null;
let pending = null;
let expiresAt = 0;
async function refresh() {
const snapshot = await deps.buildSnapshot();
const view = buildControlPlaneView(snapshot, { ...deps.viewOptions, window });
cached = { snapshot, view };
expiresAt = clock() + interval;
return cached;
}
return {
window,
async build(extra = {}) {
if (!cached || clock() >= expiresAt) {
if (!pending) pending = refresh().finally(() => { pending = null; });
await pending;
}
if (Object.keys(extra).length === 0) return cached.view;View on GitHub (pinned to 8321021c54)