Area
services/platform/convex/file_metadata/ — public mutations (saveFileMetadata, skipTranscription, retryTranscription) and the public action (transcribeDictation).
Steps to reproduce
- Upload a file that is rejected by organization policy — the mutation throws a raw Error.
- Attempt to skip transcription on an already-completed or non-running row — raw Error.
- Attempt to retry transcription on a row that is not in
failed/skipped state — raw Error.
- Record a dictation snippet larger than 8 MiB — raw Error from
transcribeDictation.
Expected
The client receives a ConvexError with a structured code field so the UI can display a user-readable message (following the pattern in tasks/mutations.ts).
Actual
throw new Error(...) is used throughout; Convex surfaces these as opaque "Server Error" messages in the browser console and the message is lost to the client.
Evidence
services/platform/convex/file_metadata/mutations.ts:38 — throw new Error('Unauthenticated') (saveFileMetadata)
services/platform/convex/file_metadata/mutations.ts:52 — throw new Error(check.reason ?? 'Upload rejected by organization policy')
services/platform/convex/file_metadata/mutations.ts:70 — throw new Error('Thread does not belong to this organization')
services/platform/convex/file_metadata/mutations.ts:269 — throw new Error('Unauthenticated') (skipTranscription)
services/platform/convex/file_metadata/mutations.ts:276 — throw new Error('File not found') (skipTranscription)
services/platform/convex/file_metadata/mutations.ts:278 — throw new Error('Not authorized') (skipTranscription)
services/platform/convex/file_metadata/mutations.ts:290 — throw new Error(\Cannot skip transcription in status "..." ...`) services/platform/convex/file_metadata/mutations.ts:323—throw new Error('Unauthenticated')(retryTranscription)services/platform/convex/file_metadata/mutations.ts:330—throw new Error('File not found')(retryTranscription)services/platform/convex/file_metadata/mutations.ts:332—throw new Error('Not authorized')(retryTranscription)services/platform/convex/file_metadata/mutations.ts:344—throw new Error(`Cannot retry transcription in status "..." ...`) services/platform/convex/file_metadata/transcribe_dictation.ts:40—throw new Error('Unauthenticated')(transcribeDictation action)services/platform/convex/file_metadata/transcribe_dictation.ts:56—throw new Error('Dictation audio exceeds 8 MiB limit')`
Notes
Related: #1993 (same pattern in websites/customers/vendors), #2000 (same pattern in knowledge_entries). The internal-action errors in transcribe_audio.ts:390,412 are scheduler-invoked pipeline errors (not direct client calls) and are appropriately left as raw Error.
Severity: medium — upload policy rejections and transcription skip/retry state violations surface as opaque "Server Error" with no user-readable feedback.
Area
services/platform/convex/file_metadata/— public mutations (saveFileMetadata,skipTranscription,retryTranscription) and the public action (transcribeDictation).Steps to reproduce
failed/skippedstate — raw Error.transcribeDictation.Expected
The client receives a
ConvexErrorwith a structuredcodefield so the UI can display a user-readable message (following the pattern intasks/mutations.ts).Actual
throw new Error(...)is used throughout; Convex surfaces these as opaque "Server Error" messages in the browser console and the message is lost to the client.Evidence
services/platform/convex/file_metadata/mutations.ts:38—throw new Error('Unauthenticated')(saveFileMetadata)services/platform/convex/file_metadata/mutations.ts:52—throw new Error(check.reason ?? 'Upload rejected by organization policy')services/platform/convex/file_metadata/mutations.ts:70—throw new Error('Thread does not belong to this organization')services/platform/convex/file_metadata/mutations.ts:269—throw new Error('Unauthenticated')(skipTranscription)services/platform/convex/file_metadata/mutations.ts:276—throw new Error('File not found')(skipTranscription)services/platform/convex/file_metadata/mutations.ts:278—throw new Error('Not authorized')(skipTranscription)services/platform/convex/file_metadata/mutations.ts:290—throw new Error(\Cannot skip transcription in status "..." ...`)services/platform/convex/file_metadata/mutations.ts:323—throw new Error('Unauthenticated')(retryTranscription)services/platform/convex/file_metadata/mutations.ts:330—throw new Error('File not found')(retryTranscription)services/platform/convex/file_metadata/mutations.ts:332—throw new Error('Not authorized')(retryTranscription)services/platform/convex/file_metadata/mutations.ts:344—throw new Error(`Cannot retry transcription in status "..." ...`)services/platform/convex/file_metadata/transcribe_dictation.ts:40—throw new Error('Unauthenticated')(transcribeDictation action)services/platform/convex/file_metadata/transcribe_dictation.ts:56—throw new Error('Dictation audio exceeds 8 MiB limit')`Notes
Related: #1993 (same pattern in websites/customers/vendors), #2000 (same pattern in knowledge_entries). The internal-action errors in
transcribe_audio.ts:390,412are scheduler-invoked pipeline errors (not direct client calls) and are appropriately left as rawError.Severity: medium — upload policy rejections and transcription skip/retry state violations surface as opaque "Server Error" with no user-readable feedback.