{"record":{"id":"13542faab988702d","repo":"ruvnet/ruflo","slug":"invalid-state-transition-from-from-to-to","errorCode":null,"errorMessage":"Invalid state transition from '${from}' to '${to}'. Allowed transitions from '${from}': ${allowed?.join(', ') ?? 'none'}","messagePattern":"Invalid state transition from '(.+?)' to '(.+?)'\\. Allowed transitions from '(.+?)': (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/testing/src/helpers/assertion-helpers.ts","lineNumber":487,"sourceCode":"/**\n * Assert state transition is valid\n *\n * @example\n * assertValidStateTransition(\n *   'pending',\n *   'running',\n *   { pending: ['running', 'cancelled'], running: ['completed', 'failed'] }\n * );\n */\nexport function assertValidStateTransition<T extends string>(\n  from: T,\n  to: T,\n  allowedTransitions: Record<T, T[]>\n): void {\n  const allowed = allowedTransitions[from];\n\n  if (!allowed || !allowed.includes(to)) {\n    throw new Error(\n      `Invalid state transition from '${from}' to '${to}'. ` +\n      `Allowed transitions from '${from}': ${allowed?.join(', ') ?? 'none'}`\n    );\n  }\n}\n\n/**\n * Assert that a retry policy was followed\n *\n * @example\n * assertRetryPattern(mockFn, { attempts: 3, backoffPattern: 'exponential' });\n */\nexport function assertRetryPattern(\n  mock: Mock,\n  options: RetryPatternOptions\n): void {\n  const calls = mock.mock.calls;\n","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/testing/src/helpers/assertion-helpers.ts#L469-L505","documentation":"assertValidStateTransition() checks that to is listed in allowedTransitions[from]; it throws when from has no entry at all or to is not permitted, printing the allowed set or none. It encodes a state-machine table in tests, so a failure means the code under test attempted (or was claimed to attempt) an illegal transition.","triggerScenarios":"Asserting pending to completed when the table only allows pending to running or cancelled; a from value that is not a key in the table (reads as no transitions); enum string mismatches such as Running vs running.","commonSituations":"State machine extended with new states but the test table not updated; transition maps copied and drifting from the production machine; renames or casing changes after refactors.","solutions":["Compare the thrown allowed list against the attempted transition; either the code took a bad path or the table is stale","Add the missing state key or transition pair to allowedTransitions if the transition is now legal","Derive the test table from the same constant the production state machine uses"],"exampleFix":"// before\nassertValidStateTransition('pending', 'completed', TRANSITIONS); // throws\n\n// after\nassertValidStateTransition('pending', 'running', TRANSITIONS);\n// or, if skipping is now legal: TRANSITIONS.pending.push('completed')","handlingStrategy":"type-guard","validationCode":"function isAllowedTransition<T extends string>(from: T, to: T, table: Record<T, T[]>): boolean {\n  return table[from]?.includes(to) ?? false;\n}\n\nif (!isAllowedTransition(from, to, TRANSITIONS)) {\n  throw new Error(`Refusing illegal transition ${from} -> ${to}`);\n}","typeGuard":"function isAllowedTransition<T extends string>(from: T, to: T, table: Record<T, T[]>): to is T {\n  return table[from]?.includes(to) ?? false;\n}","tryCatchPattern":null,"preventionTips":["Single-source the transition table shared by runtime and tests","Type the table as Record<T, T[]> so a missing state key is a compile error","Add a test that enumerates every declared transition, not just the happy path"],"tags":["testing","assertion","state-machine","transitions"],"backgroundTag":"invalid-state-transition","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}