FTP File Write Failure – Random ff_errno = 29 and HardFault Causing Device Restart

Hello FreeRTOS Team,

I am encountering an issue while using the FTP server provided in FreeRTOS+FAT. During file write operations over FTP, the transfer randomly fails. When the failure occurs, the FTP server logs the following error:

ff_errno = 29 // Derived from prvFFErrorToErrno()

This error appears to be intermittent and does not consistently occur at the same point in the transfer. The problem is more frequent when writing larger files or during multiple consecutive transfers.

Additionally, we have observed that sometimes the system experiences a HardFault during or shortly after the FTP transfer failure. When this occurs:

  • The device restarts unexpectedly.
  • Our watchdog counter indicates that a reset was triggered due to a watchdog trip.
  • This behavior is observed only in normal runtime; the issue does not reproduce in debug mode.

Some context about our setup:

  • FreeRTOS version: FreeRTOS V9.0.0
  • FreeRTOS+FAT version: FreeRTOS V9.0.0
  • FTP Server source: FreeRTOS_FTP_server.c
  • FTP handling logic is managed via prvStoreFileWork().

We would appreciate your help in understanding:

  • What does ff_errno = 29 specifically indicate?
  • Are there known conditions in which the FTP server or file system could cause a HardFault or memory corruption?
  • Any guidance on how to debug or trace this kind of issue?

Thank you for your support.

Best regards,
Arun G K

Hi @Arun
Welcome to the FreeRTOS Community Forums !

  1. ff_errno = 29 refers to Illegal seek as mentioned here . This can occur when trying to read or write out of the file’s space.
  2. Stack overflows, buffer overruns , heap corruption, and file system corruption under concurrent load or large transfers further increase the risk of memory corruption and crashes.
  3. You should enable Assert and STACK overflow checking in your application to look for potential pitfalls.
    a. Enable all configASSERT() and ffconfigASSERT() checks.
    b. #define configCHECK_FOR_STACK_OVERFLOW 2

Also, you are using FreeRTOS v9.0.0 and FreeRTOS+FAT from that version - many fixes and enhancements have been made in later versions of +FAT. You might consider using a newer version if feasible.

Also this a good guide to debug hard fault - https://www.keil.com/appnotes/files/apnt209.pdf. You may have to stop your watchdog so that the device is not restarted while you are debugging the fault.

This issue occurs intermittently during the publishing of large-sized web pages and when transferring large files via FileZilla.

In our code, the condition if( xWritten != xRc ) is being triggered, indicating a mismatch between the number of bytes received and those successfully written to the file. This suggests a potential write failure during the file transfer.

Below is the relevant portion of the code from prvStoreFileWork():

static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )
{
    BaseType_t xRc, xWritten;

    for( ; ; )
    {
        char *pcBuffer;

        // Zero-copy receive
        xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,
                             0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );
        if( xRc <= 0 )
        {
            break;
        }

        pxClient->ulRecvBytes += xRc;
        xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );

        // Return buffer to the stack
        FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );

        if( xWritten != xRc )
        {
            xRc = -1;
            pxClient->bits1.bHadError = pdTRUE_UNSIGNED;
            break;
        }
    }
    return xRc;
}

For further analysis, please refer to the attached Wireshark trace and the screenshots below:

FTP Failure During File Transfer:
A failure occurs while transferring files over FTP.

Error When Deleting Existing WebMI Directory:
An issue arises when attempting to delete the existing WebMI directory.

Not sure if it is related to the error you are seeing but the above does not seem correct. Refer to the following page - Receiving UDP Data (zero copy interface) - FreeRTOS™. Can you turn off zero copy for testing?

I tried disabling zero-copy, but I’m still encountering a HardFault.

It seems the issue occurs during the write operation:
I’m currently seeing intermittent ff_errno = 29 errors during a call to ff_fwrite():
xWritten = ff_fwrite(pcBuffer, 1, xRc, pxClient->pxWriteHandle);

Here’s the relevant part of the ff_fwrite() implementation:

size_t ff_fwrite( const void *pvBuffer, size_t xSize, size_t xItems, FF_FILE * pxStream )
{
int32_t iReturned;
size_t xReturn;
int ff_errno;

#if( ffconfigDEV_SUPPORT != 0 )
	if( pxStream->pxDevNode != NULL )
	{
		iReturned = FF_Device_Write( pvBuffer, xSize, xItems, pxStream );
	}
	else
#endif
	{
		iReturned = FF_Write( pxStream, xSize, xItems, (uint8_t *)pvBuffer );
	}

	ff_errno = prvFFErrorToErrno( iReturned );

	if( ff_errno == pdFREERTOS_ERRNO_NONE )
	{
		/* As per the standard fwrite() semantics, the return value is the
		number of complete items read, which will only equal the number of bytes
		transferred when the item size is 1. */
		xReturn = ( size_t ) iReturned;
	}
	else
	{
		xReturn = 0;
	}

	/* Store the errno to thread local storage. */
	stdioSET_ERRNO( ff_errno );

	return xReturn;
}

The error is sporadic, but when it happens, ff_errno is consistently 29 .
This also sometimes causes a HardFault.

Could you please help me identify what might be causing this and how best to debug it?

You must have seen that

#define pdFREERTOS_ERRNO_ESPIPE           29  /* Illegal seek */

And FF_Write() calls FF_Seek() in case the file was opened with the flag FF_MODE_APPEND.

A file shall be opened in append-mode if the client sends a

if( pxClient->ulRestartOffset != 0 )

which happens when the client sends the ECMD_REST command: recover from a broken upload.

Can you confirm that this occurs?

What low-level driver are you using? On of the drivers found here?
Maybe there is some data corruption?
Please tell which driver you are using.

I just tested the FTP/REST command by sending a large file to the DUT. I interrupted the transmission when half-way by exiting FileZilla.

Just to be sure, I reset the device, started FileZilla and I pressed “Process Queue” to continue transmission. I chose Action->Resume to send the remainder of the file.

Then I compared the two files: they were exactly the same.

If you think that the REST command doesn’t correctly, you might have FileZilla produce detailed logging.

Can you confirm that this occurs?
No, it is not occurring.

  • Controller: STM32F746NE
  • FreeRTOS version: FreeRTOS V9.0.0
  • FreeRTOS+FAT version: FreeRTOS V9.0.0

What low-level driver are you using? One of the drivers found here?
Yes, we are using the same drivers.

Maybe there is some data corruption?
No, data is not corrupting

Please tell me which driver you are using.
STM32 HAL drivers.

This is a random issue that occurs approximately 3 to 4 times out of every 10 attempts.
Wireshark_trace.zip (2.3 MB)

I tested Stack Overflow; heap corruption is not happening.

void vApplicationMallocFailedHook( void )
{
	/* vApplicationMallocFailedHook() will only be called if
	configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h.  It is a hook
	function that will get called if a call to pvPortMalloc() fails.
	pvPortMalloc() is called internally by the kernel whenever a task, queue,
	timer or semaphore is created.  It is also called by various parts of the
	demo application.  If heap_1.c or heap_2.c are used, then the size of the
	heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
	FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
	to query the size of free heap space that remains (although it does not
	provide information on how the remaining heap might be fragmented). */
        printf("Malloc failed!\n");
	taskDISABLE_INTERRUPTS();
	for( ;; );
}	/*end of vApplicationMallocFailedHook()*/

void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
	( void ) pcTaskName;
	( void ) pxTask;

	/* Run time stack overflow checking is performed if
	configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2.  This hook
	function is called if a stack overflow is detected. */
        printf("Stack overflow in task: %s\n", pcTaskName);
	taskDISABLE_INTERRUPTS();
	for( ;; );
}	/*end of vApplicationStackOverflowHook()*/
---------------------------------------------------------------------------------------------------
static BaseType_t prvStoreFilePrep( FTPClient_t *pxClient, char *pcFileName )
{
BaseType_t xResult;
FF_FILE *pxNewHandle;
size_t uxFileSize = 0ul;
int iErrorNo;

	/* Close previous handle (if any) and reset file transfer parameters. */
	prvTransferCloseFile( pxClient );

	xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );

	pxNewHandle = NULL;

	if( pxClient->ulRestartOffset != 0 )
	{
	size_t uxOffset = pxClient->ulRestartOffset;
	int32_t lRc;
         printf("[FTP] Append mode. Restart offset = %lu\n", pxClient->ulRestartOffset);
         printf("[FTP] Opening file: %s in 'ab' mode\n", pxClient->pcFileName);

		pxClient->ulRestartOffset = 0ul; /* Only use 1 time. */
		pxNewHandle = ff_fopen( pxClient->pcFileName, "ab" );

		if( pxNewHandle != NULL )
		{
			uxFileSize = pxNewHandle->ulFileSize;

			if( uxOffset <= uxFileSize )
			{
				lRc = ff_fseek( pxNewHandle, uxOffset, FF_SEEK_SET );
			}
			else
			{
				/* Won't even try to seek after EOF */
				lRc = -pdFREERTOS_ERRNO_EINVAL;
			}
			if( lRc != 0 )
			{
			BaseType_t xLength;

				xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
					"450 Seek invalid %u length %u\r\n",
					( unsigned ) uxOffset, ( unsigned ) uxFileSize );

				/* "Requested file action not taken". */
				prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );

				FreeRTOS_printf( ( "ftp::storeFile: create %s: Seek %u length %u\n",
					pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );

				ff_fclose( pxNewHandle );
				pxNewHandle = NULL;
			}
		}
	}
	else
	{
		pxNewHandle = ff_fopen( pxClient->pcFileName, "wb" );
	}

	if( pxNewHandle == NULL )
	{
		iErrorNo = stdioGET_ERRNO();
		if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )
		{
			prvSendReply( pxClient->xSocket, REPL_552, 0 );
		}
		else
		{
			/* "Requested file action not taken". */
			prvSendReply( pxClient->xSocket, REPL_450, 0 );
		}
		FreeRTOS_printf( ( "ftp::storeFile: create %s: %s (errno %d)\n",
			pxClient->pcFileName,
			( const char* ) strerror( iErrorNo ), iErrorNo ) );

		xResult = pdFALSE;
	}
	else
	{
		if( pxClient->bits1.bIsListen )
		{
			/* True if PASV is used. */
			snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),
				"150 Accepted data connection from %%xip:%%u\r\n" );
			prvTransferCheck( pxClient );
		}
		else
		{
		BaseType_t xLength;

			xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150 Opening BIN connection to store file\r\n" );
			prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
			pxClient->pcConnectionAck[ 0 ] = '\0';
			prvTransferStart( pxClient ); /* Now active connect. */
		}

		pxClient->pxWriteHandle = pxNewHandle;

		/* To get some statistics about the performance. */
		pxClient->xStartTime = xTaskGetTickCount( );

		xResult = pdTRUE;
	}

	return xResult;
}
-------------------------------------------------------------------------------------------------------
static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )
	{
	BaseType_t xRc, xWritten;
        int error = 0;

		/* Read from the data socket until all has been read or until a negative value
		is returned. */
		for( ; ; )
		{
		char *pcBuffer;

			/* The "zero-copy" method: */
			xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,
				0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );
			if( xRc <= 0 )
			{
				break;
			}
			pxClient->ulRecvBytes += xRc;
			xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );
			FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );
			if( xWritten != xRc )
			{
                          error = stdioGET_ERRNO();
                          printf("error: %d\n", error);
				xRc = -1;
				/* bHadError: a transfer got aborted because of an error. */
				pxClient->bits1.bHadError = pdTRUE_UNSIGNED;
				break;
			}
		}
		return xRc;
	}

Above the function, I added print statements inside the condition if( pxClient->ulRestartOffset != 0 ), but nothing is being printed there. The output is only appearing in the function prvStoreFileWork.

Output is:
error: 29

Please let me know if there’s anything I might be missing or doing incorrectly.

May I kindly request an update on this issue when you get a chance?

I would like to know the cause, where does errno 29 come from?

In ff_stdio.c:

if( xError == FF_ERR_FILE_SEEK_INVALID_POSITION )
{
    /* Illegal position, outside the file's space */
    ff_errno = pdFREERTOS_ERRNO_ESPIPE;
}

But also in prvFFErrorToErrno( FF_Error_t xError ):

case FF_ERR_IOMAN_OUT_OF_BOUNDS_READ:
    return pdFREERTOS_ERRNO_ESPIPE;

case FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE:
    return pdFREERTOS_ERRNO_ESPIPE;

case FF_ERR_FILE_SEEK_INVALID_POSITION:
    return pdFREERTOS_ERRNO_ESPIPE; /* Illegal position, outside the file's space */

Now if you run the project from a debugger, can you put a few breaks and see where the error is set?

Historically +FAT comes with its own set of error codes. Later, when making it stdio compatible, the function prvFFErrorToErrno() was added to do the translations.

I do have my own version of the STM32F7xx driver. I use it in professional projects, and I haven’t seen any problems with it. Also it allows to use a different hw connection to the CPU.

EDIT See my next post to find the newer driver.

I uploaded my newer driver for the STM32F7x SD-card here.

Although it is called STM32F7xx_slot.2, it works for both slot 1 and slot 2.

For the FreeRTOS demos using SDMMC you can define:

    #define STM_USE_SDMMC2   0

As I am using the other slot, I define:

    #define STM_USE_SDMMC2   1

( in the code, only STM_USE_SDMMC2 is being tested ).

But also, I would like to see where errno will be set to 29.