mifi/lossless-cut · error · UserFacingError

Expression did not lead to a string

Error message

Expression did not lead to a string

What it means

Thrown by the output-filename template evaluator in outputNameTemplate.ts when safeishEval() of the user's template literal returns a value that is not a string. The template is interpolated inside a JS template string with the FileNameTemplateContext, and if the entire expression evaluates to a non-string (e.g. a number, object, array, or undefined) the result cannot be used as a filename. It is a post-eval type guard.

Source

Thrown at src/renderer/src/util/outputNameTemplate.ts:219

    [selectedSegNumVariable]: selectedSegNumPadded,
    SEG_LABEL: segLabels.length === 1 ? segLabels[0] : segLabels,
    EPOCH_MS: epochMs,
    CUT_FROM: cutFromStr,
    CUT_FROM_NUM: cutFrom,
    CUT_TO: cutToStr,
    CUT_TO_NUM: cutTo,
    CUT_DURATION: cutDurationStr,
    [segTagsVariable]: tags && {
      // allow both original case and uppercase
      ...tags,
      ...Object.fromEntries(Object.entries(tags).map(([key, value]) => [`${key.toLocaleUpperCase('en-US')}`, value])),
    },
    FILE_EXPORT_COUNT: currentFileExportCount != null ? currentFileExportCount + 1 : undefined,
    EXPORT_COUNT: exportCount != null ? exportCount + 1 : undefined,
  } satisfies FileNameTemplateContext;

  const ret = (await safeishEval(`\`${template}\``, context));
  if (typeof ret !== 'string') throw new UserFacingError(i18n.t('Expression did not lead to a string'));
  return ret;
}

function maybeTruncatePath(fileName: string, truncate: boolean) {
  // Split the path by its separator, so we can check the actual file name (last path seg)
  const pathSegs = fileName.split(pathSep);
  if (pathSegs.length === 0) return '';
  const [lastSeg] = pathSegs.slice(-1);
  const rest = pathSegs.slice(0, -1);

  return [
    ...rest,
    // If sanitation is enabled, make sure filename (last seg of the path) is not too long
    truncate ? lastSeg!.slice(0, maxFileNameLength) : lastSeg,
  ].join(pathSep);
}

async function generateWithFallback({ generate, desiredTemplate, defaultTemplate, safeOutputFileName, filePath, outputDir, maxLabelLength }: {

View on GitHub (pinned to 3b9a59c288)

Solutions

  1. Wrap the whole template expression so it always yields a string, e.g. prefix/suffix with literal text or use String(): '${String(CUT_FROM_NUM)}'.
  2. Avoid using a bare numeric/array variable as the entire template; combine it with static text.
  3. For SEG_LABEL, ensure a single label is selected or coerce: '${Array.isArray(SEG_LABEL) ? SEG_LABEL.join('-') : SEG_LABEL}'.
  4. Open the output filename template editor and test the template against the current segment to see the evaluated value.

Example fix

// before (template user typed): ${CUT_FROM_NUM}

// after (template): cut-${CUT_FROM_NUM}
Defensive patterns

Strategy: validation

Validate before calling

// Validate that the template evaluates to a string before relying on it
export async function evalTemplateToString(template: string, ctx: object): Promise<string> {
  const ret = await safeishEval(`\`${template}\``, ctx);
  if (typeof ret !== 'string') {
    throw new Error(`Template did not produce a string (got ${ret === undefined ? 'undefined' : Array.isArray(ret) ? 'array' : typeof ret}). Add static text or coerce with String().`);
  }
  return ret;
}

Type guard

const evaluatesToString = (v: unknown): v is string => typeof v === 'string';

Try / catch

try {
  fileName = await evalFileNameTemplate(template, context);
} catch (err) {
  if (err instanceof UserFacingError && /did not lead to a string/.test(err.message)) {
    showError('Filename template produced a non-string value. Combine it with literal text.');
    fileName = 'output';
  }
}

Prevention

When it happens

Trigger: A filename template whose top-level expression is a bare variable holding a non-string (e.g. '${CUT_FROM_NUM}' yielding a number, or '${SEG_LABEL}' yielding an array when multiple labels exist); a template that resolves to undefined because every variable used was optional and unset; a template that returns an object literal.

Common situations: User writes a template referencing a numeric context variable without coercion (e.g. just '${EPOCH_MS}'); SEG_LABEL evaluates to an array when more than one segment label is present; a template referencing an undefined variable that produces undefined; arithmetic in the template yielding a non-string.

Related errors


AI-assisted analysis of mifi/lossless-cut@3b9a59c288 (2026-08-12). Data as JSON: /api/errors/1d65d5f985b38778. Report an issue: GitHub.